Detecting Process Injection using a debugger (x64bdg)
What is Process Injection?
Process Injection is a technique used by attackers to run malicious code within the address space of another legitimate process. This makes the malicious activity blend in with normal system operations, making it harder to detect. Essentially, instead of creating a new process that might raise suspicion, the attacker “injects” their code into an existing process that’s already running on the system.
How does Process Injection work?
- Identify a Target Process: The attacker first identifies a legitimate process on the victim’s machine. Common targets are processes that are always running and trusted by the system, let's say notepad.exe
- Gain Access to the Process: The attacker then needs to gain access to the target process. This involves using special functions the operating system provides, like OpenProcess, which allows one process to interact with another.
- Allocate Memory in the Target Process: The attacker allocates memory within the target process where the malicious code will be placed. This is done using functions like VirtualAllocEx, which reserves space in the target process’s memory.
- Write the Malicious Code: The malicious code is then written into the allocated memory. The attacker uses functions like WriteProcessMemory to insert their code into the memory space of the target process.
- Execute the Malicious Code: Finally, the attacker needs to execute the injected code. This can be done using various methods, such as creating a new thread in the target process using CreateRemoteThread, or by hijacking an existing thread and redirecting it to execute the injected code.
Why often do malware authors use process injections in their malware?
- Evasion of Detection: By running their malicious code within a trusted process, attackers can avoid detection by security software. Since the code runs within a legitimate process, it’s harder for antivirus programs to distinguish between normal activity and malicious activity.
- Stealth and Persistence: Injected code can persist as long as the legitimate process is running. This makes it difficult to identify and remove the malware, as killing the legitimate process might disrupt normal system operations.
- Privilege Escalation: Some processes run with higher privileges than others. By injecting code into a process with higher privileges, an attacker can elevate the permissions of their code, gaining more control over the system.
- Access to System Resources: Legitimate processes often have access to system resources, such as files, network connections, and other processes. By injecting code into these processes, the malware can leverage these resources without raising suspicion.
Introduction to API calls made during process injection?
Key API Calls in Process Injection
- OpenProcess
Purpose: The OpenProcess API call is used to gain a handle (a reference) to a specific process running on the system. This handle allows the attacker to interact with the target process in various ways such as reading or writing to its memory.
How it Works: The attacker needs to specify the process ID (PID) of the target process and the desired level of access (e.g., to read, write, or execute). If successful, OpenProcess returns a handle that can be used in subsequent API calls.
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess, //Access rights to the target process
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId //Process ID of the target process
);
2. VirtualAllocEx:
Purpose: VirtualAllocEx is used to allocate memory within the address space of the target process. This memory is where the attacker’s malicious code will be injected.
How it Works: The attacker specifies the size of the memory they want to allocate and the protection level (e.g., read, write, or execute). VirtualAllocEx then reserves a block of memory in the target process’s space, which can be used to store the malicious code.
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress, //base address where the shell code needs to be deployed in target process
[in] SIZE_T dwSize, //size of the shellcode/bytes to be written in this region
[in] DWORD flAllocationType,
[in] DWORD flProtect
3. WriteProcessMemory:
Purpose: WriteProcessMemory writes data to the memory space of the target process. This is the step where the actual malicious code (or payload) is injected into the allocated memory.
How it Works: The attacker specifies the handle of the target process, the address of the allocated memory (from VirtualAllocEx), and the data to be written (the malicious code). WriteProcessMemory then copies the data into the target process’s memory.
BOOL WriteProcessMemory(
[in] HANDLE hProcess, //access to modify the target process memory region
[in] LPVOID lpBaseAddress, //base address where the shell code needs to be deployed in target process
[in] LPCVOID lpBuffer, //address of the data in source process that is to be written in the target process
[in] SIZE_T nSize, //number of bytes to be written in the target process
[out] SIZE_T *lpNumberOfBytesWritten //number of bytes successfully written
4. CreateRemoteThread
Purpose: CreateRemoteThread is used to create a new thread within the target process. This thread is configured to start execution from the address where the malicious code was written, effectively running the injected code.
How it Works: The attacker specifies the handle of the target process, the starting address (where the malicious code resides), and other thread parameters. CreateRemoteThread then instructs the target process to start executing the code at the specified memory address.
HANDLE CreateRemoteThread(
[in] HANDLE hProcess, //access to modify the target process memory region
[in] LPSECURITY_ATTRIBUTES lpThreadAttributes, //represents the starting address of the thread in the target process.
[in] SIZE_T dwStackSize,
[in] LPTHREAD_START_ROUTINE lpStartAddress,
[in] LPVOID lpParameter,
[in] DWORD dwCreationFlags,
[out] LPDWORD lpThreadId
);
Detecting process injection using x64dbg
To begin with open x64dbg and load the malicious sample to be analyzed (in this case we will be going through self-created malware that executes code into notepad.exe)
Into the symbols tab:
Select > kernel32.dll
Search > VirtualAllocEx > Toggle Breakpoint
Search > WriteProcessMemory > Toggle Breakpoint
Search > CreateRemoteThread > Toggle Breakpoint
Press F9 or the Run arrow on the taskbar until the execution flow reaches VirtualAllocEx, by this time notepad will be spawned (this can be confirmed using Process Hacker)
Again press F9 until the execution pointer stops on WriteProcessMemory. At this point the lpBaseaddress value that will be pushed into the function parameter will be in the RDX register
Open another instance of x64debug and attach the notepad.exe process to this instance
Copy the RDX register value from the x64dbg-1 and paste this value into x64dbg-2:
Ctrl + G > paste
Right Click on the address > follow in dump
At this point, you will see no code in the address space
Now press F9 in the x64dbg-1(malware.exe). Once you press F9 the pointer will stop at the CreateRemoteThread function call.
Open x64dbg-2, you will now see a shellcode present at that address space which was blank previously before the execution of WriteProcessMemory
Each value in the shellcode pre represents CPU instruction that needs to be executed.
References: