├── README.md └── main.c /README.md: -------------------------------------------------------------------------------- 1 | # injector 2 | Code Injector Using Code Caves 3 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | Enter file contents here#include "global.h" 2 | 3 | #define MIN_VM_ACCESS_MASK ( PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION) 4 | 5 | UCHAR sg_pFullImageHdr[PAGE_SIZE]; 6 | 7 | NTSTATUS openProcByName(PHANDLE pProcess, PUNICODE_STRING pProcName, BOOLEAN useDebugPrivilege){ 8 | SYSTEM_PROCESS_INFORMATION procInfo; 9 | OBJECT_ATTRIBUTES procAttr; 10 | OBJECT_BASIC_INFORMATION processHandleInfo; 11 | CLIENT_ID cid; 12 | BOOLEAN oldValue; 13 | HANDLE pid; 14 | 15 | NTSTATUS status = STATUS_CACHE_PAGE_LOCKED; 16 | ULONG procListSize = 0; 17 | ULONGLONG memSize = 0; 18 | ULONG obQueryLen = 0; 19 | PVOID pProcListHead = NULL; 20 | PSYSTEM_PROCESS_INFORMATION pProcEntry = NULL; 21 | 22 | if (!pProcName || !pProcess) 23 | return STATUS_INVALID_PARAMETER; 24 | 25 | *pProcess = NULL; 26 | 27 | ///Since we specify a buffer size of 0 the buffer must overflow for sure even if there was running a 28 | ///single process only. If we don't receive the dedicated error, something other has gone wrong 29 | ///and we cannot rely on the return length. 30 | status = NtQuerySystemInformation(SystemProcessInformation, &procInfo, procListSize, &procListSize); 31 | if (STATUS_INFO_LENGTH_MISMATCH != status) 32 | return status; 33 | 34 | memSize = PAGE_ROUND_UP(procListSize) + PAGE_SIZE; ///We better allocate one page extra 35 | ///since between our "test" call and the real call below 36 | ///additional processes might be started. (race condition) 37 | status = NtAllocateVirtualMemory(INVALID_HANDLE_VALUE, &pProcListHead, 0, &memSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 38 | if (status) 39 | return status; 40 | 41 | ///By now, we have allocated a buffer large enough for the complete process list, 42 | ///even if some new processes have been started in the mean time. 43 | ///Hence, the next call is entirely expected to succeed. 44 | procListSize = (ULONG)memSize; 45 | status = NtQuerySystemInformation(SystemProcessInformation, pProcListHead, procListSize, &procListSize); 46 | if (status){ 47 | memSize = 0; 48 | NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pProcListHead, &memSize, MEM_RELEASE); 49 | return status; 50 | } 51 | 52 | pid = NULL; 53 | pProcEntry = pProcListHead; ///The list of all system processes is a so called singly linked list. 54 | while (pProcEntry->NextEntryOffset){ ///If NextEntryOffset member is NULL, we have reached the list end (tail). 55 | pProcEntry = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)pProcEntry + pProcEntry->NextEntryOffset); 56 | //DebugPrint2A("PID: %d, %wZ", pProcEntry->UniqueProcessId, pProcEntry->ImageName); 57 | if (0 == RtlCompareUnicodeString(pProcName, &pProcEntry->ImageName, TRUE)){ 58 | pid = pProcEntry->UniqueProcessId; 59 | break; 60 | } 61 | } 62 | 63 | memSize = 0; 64 | NtFreeVirtualMemory(INVALID_HANDLE_VALUE, &pProcListHead, &memSize, MEM_RELEASE); ///We don't need the list anymore. 65 | 66 | if (!pid) 67 | return STATUS_OBJECT_NAME_NOT_FOUND; 68 | 69 | //DebugPrint2A("%wZ pid = %llu", *pProcName, pid); 70 | 71 | if (useDebugPrivilege){ 72 | status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &oldValue); 73 | if (status) ///Since we're for some reason supposed to use the SeDebugPrivilege, 74 | return status; ///we fail deliberately if we can't enable it. 75 | } 76 | 77 | InitializeObjectAttributes(&procAttr, NULL, 0, NULL, NULL); 78 | cid.UniqueThread = (HANDLE)0; 79 | cid.UniqueProcess = pid; 80 | ///Opening a process for full access might be less suspicious than opening with our real intentions. 81 | status = NtOpenProcess(pProcess, PROCESS_ALL_ACCESS, &procAttr, &cid); 82 | 83 | if (useDebugPrivilege) 84 | ///We don't have any clue if the privilege already was enabled, 85 | ///so we simply restore the old status. Whether we do this call or not 86 | ///isn't anyhow related to the result of process opening. 87 | RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, oldValue, FALSE, &oldValue); 88 | 89 | if (status) 90 | return status; ///Most likely STATUS_ACCESS_DENIED if 91 | ///either we didn't specify the useDebugPrivilege flag when opening a cross session process 92 | ///or if we tried to open an elevated process while running non-elevated. 93 | 94 | ///In x64 windows, HIPS or AV drivers have the possibility to legally 95 | ///receive a notification if a process is about to open a handle to another process. 96 | ///In those ObCallback routines they cannot completely deny the opening. 97 | ///However, they are able to modify the access masks, so a handle supposed for VM operations still 98 | ///will be lacking the PROCESS_VM_XXX rights, for example. If we therefore query the handle rights 99 | ///we can still return an appropriate error if wasn't granted the rights we want 100 | ///And are not going to fail at first when performing our process operations. 101 | status = NtQueryObject(*pProcess, ObjectBasicInformation, &processHandleInfo, sizeof(OBJECT_BASIC_INFORMATION), &obQueryLen); 102 | if (status){ ///Not sure if this call ever will fail... 103 | NtClose(*pProcess); 104 | *pProcess = NULL; 105 | return status; 106 | } 107 | 108 | ///Maybe, HIPS just wanted to deny PROCESS_TERMINATE/PROCESS_SUSPEND right? 109 | ///If so, we don't care. We're only interested in VM rights. 110 | if (MIN_VM_ACCESS_MASK & ~processHandleInfo.GrantedAccess){ 111 | NtClose(*pProcess); 112 | *pProcess = NULL; 113 | return STATUS_UNSUCCESSFUL; 114 | } 115 | 116 | return STATUS_SUCCESS; 117 | } 118 | 119 | 120 | 121 | NTSTATUS findCodeCave(HANDLE hProcess, PVOID* pCodeCave, ULONGLONG desiredSize, PULONGLONG pActualSize){ 122 | MEMORY_BASIC_INFORMATION freeMemInfo; 123 | MEMORY_BASIC_VLM_INFORMATION imageOrMappingInfo; 124 | IMAGE_SECTION_HEADER currSecHdr; 125 | ULONG oldProt; 126 | 127 | NTSTATUS status = STATUS_UNSUCCESSFUL; 128 | PVOID pCurrAddress = NULL; 129 | PIMAGE_NT_HEADERS64 pPeHdr64 = NULL; 130 | ULONGLONG resultLen = 0; 131 | BOOLEAN queryFreeMem = FALSE; 132 | ULONGLONG fullImageHdrSize = PAGE_SIZE; 133 | 134 | PIMAGE_SECTION_HEADER pFirstSecHdr = NULL; 135 | 136 | if (!pActualSize || !desiredSize || !pActualSize || !pCodeCave) 137 | return STATUS_INVALID_PARAMETER; 138 | 139 | desiredSize = ALIGN_UP(desiredSize, 0x10); 140 | *pActualSize = 0; 141 | RtlZeroMemory(&freeMemInfo, sizeof(MEMORY_BASIC_INFORMATION)); 142 | RtlZeroMemory(&imageOrMappingInfo, sizeof(MEMORY_BASIC_VLM_INFORMATION)); 143 | 144 | for (;;){ 145 | if (queryFreeMem) 146 | status = NtQueryVirtualMemory(hProcess, pCurrAddress, MemoryBasicInformation, &freeMemInfo, sizeof(MEMORY_BASIC_INFORMATION), &resultLen); 147 | else 148 | status = NtQueryVirtualMemory(hProcess, pCurrAddress, MemoryBasicVlmInformation, &imageOrMappingInfo, sizeof(MEMORY_BASIC_VLM_INFORMATION), &resultLen); 149 | if (STATUS_INVALID_ADDRESS == status){ 150 | queryFreeMem = TRUE; 151 | continue; 152 | } 153 | if (STATUS_INVALID_PARAMETER == status) 154 | break; 155 | if (status) 156 | return status; 157 | 158 | if (queryFreeMem){ 159 | pCurrAddress = (PUCHAR)pCurrAddress + freeMemInfo.RegionSize; 160 | queryFreeMem = FALSE; 161 | continue; 162 | } 163 | else { 164 | pCurrAddress = (PUCHAR)pCurrAddress + imageOrMappingInfo.SizeOfImage; 165 | queryFreeMem = FALSE; 166 | } 167 | 168 | if (MEM_IMAGE != imageOrMappingInfo.Type) 169 | continue; 170 | 171 | OutputDebugStringA("Memory Basic Vlm Info | Found an Image!"); 172 | DebugPrint2A("memVlmInfo.Type: %llX", imageOrMappingInfo.Type); 173 | DebugPrint2A("memVlmInfo.Protection: %llX", imageOrMappingInfo.Protection); 174 | DebugPrint2A("memVlmInfo.ImageBase: %p", imageOrMappingInfo.ImageBase); 175 | DebugPrint2A("memVlmInfo.SizeOfImage: %llX", imageOrMappingInfo.SizeOfImage); 176 | DebugPrint2A("memVlmInfo.Unknown: %llX", imageOrMappingInfo.Unknown); 177 | 178 | status = NtProtectVirtualMemory(hProcess, (PVOID)&imageOrMappingInfo.ImageBase, &fullImageHdrSize, PAGE_READONLY, &oldProt); 179 | if (status) 180 | continue; 181 | 182 | DebugPrint2A("memVlmInfo.ImageBase: %p", imageOrMappingInfo.ImageBase); 183 | status = NtReadVirtualMemory(hProcess, (PVOID)imageOrMappingInfo.ImageBase, sg_pFullImageHdr, sizeof(sg_pFullImageHdr), &fullImageHdrSize); 184 | if (status) 185 | continue; 186 | 187 | pPeHdr64 = (PIMAGE_NT_HEADERS64)(sg_pFullImageHdr + ((PIMAGE_DOS_HEADER)sg_pFullImageHdr)->e_lfanew); 188 | if (IMAGE_NT_SIGNATURE != pPeHdr64->Signature) 189 | continue; 190 | 191 | pFirstSecHdr = IMAGE_FIRST_SECTION(pPeHdr64); 192 | for (ULONG i = 0; i < pPeHdr64->FileHeader.NumberOfSections; i++){ 193 | currSecHdr = pFirstSecHdr[i]; 194 | DebugPrint2A("%lX", currSecHdr.Characteristics); 195 | if (currSecHdr.Characteristics & IMAGE_SCN_MEM_EXECUTE){ 196 | DebugPrint2A("Executable section starts @ %p with size %llX", (sg_pFullImageHdr + PAGE_ROUND_UP(currSecHdr.VirtualAddress)), currSecHdr.Misc.VirtualSize); 197 | } 198 | } 199 | } 200 | return STATUS_SUCCESS; 201 | } 202 | 203 | 204 | void mymain(void){ 205 | NTSTATUS status = STATUS_GENERIC_NOT_MAPPED; 206 | HANDLE hProcess = INVALID_HANDLE_VALUE; 207 | UNICODE_STRING uProcName; 208 | PVOID pCodeCave = NULL; 209 | ULONGLONG actualSize = 0; 210 | RtlInitUnicodeString(&uProcName, L"notepad.exe"); 211 | 212 | status = openProcByName(&hProcess, &uProcName, FALSE); 213 | if (status){ 214 | NtRaiseHardError(status, 0, 0, NULL, 0, (PULONG)&status); 215 | return; 216 | } 217 | 218 | //DebugPrint2A("Success! Full or VM access handle: %p", hProcess); 219 | status = findCodeCave(hProcess, &pCodeCave, 89, &actualSize); 220 | if (status){ 221 | NtRaiseHardError(status, 0, 0, NULL, 0, (PULONG)&status); 222 | return; 223 | } 224 | } 225 | --------------------------------------------------------------------------------