Last active
February 6, 2026 10:37
-
-
Save WitherOrNot/c6fa56b943b09bdd8a23fe43cec6f4cc to your computer and use it in GitHub Desktop.
Demonstration of Warbird Heap Execute, see https://github.com/WitherOrNot/warbird-docs/blob/main/WarbirdModern.md
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
| #include <stdio.h> | |
| #include <windows.h> | |
| #include <winternl.h> | |
| #pragma comment(lib, "bcrypt.lib") | |
| #pragma comment(lib, "ntdll.lib") | |
| typedef struct _FEISTEL64_ROUND_DATA { | |
| DWORD FunctionID; | |
| DWORD Rand0; | |
| DWORD Rand1; | |
| DWORD Rand2; | |
| } FEISTEL64_ROUND_DATA; | |
| typedef struct _ENCRYPTION_BLOCK { | |
| DWORD Flags; | |
| DWORD RVA; | |
| DWORD Length; | |
| } ENCRYPTION_BLOCK; | |
| typedef struct _ENCRYPTION_SEGMENT { | |
| BYTE bHash[32]; | |
| ULONG ulStructSize; | |
| ULONG _padding0; | |
| ULONG PayloadOffset; | |
| ULONG RelocationTableOffset; | |
| ULONG ulRelocationTableLength; | |
| ULONG _padding1; | |
| ULONGLONG ullPreferredImageBase; | |
| ULONG ulSegmentID; | |
| ULONG _padding2; | |
| ULONGLONG ullKey; | |
| FEISTEL64_ROUND_DATA bRoundData[10]; | |
| DWORD cBlocks; | |
| ENCRYPTION_BLOCK Blocks[1]; | |
| } ENCRYPTION_SEGMENT; | |
| typedef struct _HEAP_EXECUTE_CALL_ARGUMENT { | |
| BYTE bHash[32]; | |
| ULONG ulStructSize; | |
| ULONG ulCheckStackSize; | |
| ULONG PayloadOffset : 28; | |
| ULONG _padding0; | |
| ULONG ulChecksum : 8; | |
| ULONG ulWrapperChecksum : 8; | |
| ULONG ulRva : 28; | |
| ULONG ulSize : 28; | |
| ULONG ulWrapperRva : 28; | |
| ULONG ulWrapperSize : 28; | |
| ULONGLONG ullKey; | |
| FEISTEL64_ROUND_DATA RoundData[10]; | |
| } HEAP_EXECUTE_CALL_ARGUMENT, * PHEAP_EXECUTE_CALL_ARGUMENT; | |
| BOOL SHACompute(PVOID Data, SIZE_T DataSize, PVOID OutHash) { | |
| NTSTATUS Status; | |
| BCRYPT_ALG_HANDLE AlgHandle = NULL; | |
| BCRYPT_HASH_HANDLE HashHandle = NULL; | |
| PBYTE Hash = NULL; | |
| DWORD HashLength = 0; | |
| DWORD ResultLength = 0; | |
| // Open an algorithm handle | |
| // This sample passes BCRYPT_HASH_REUSABLE_FLAG with BCryptAlgorithmProvider(...) to load a provider which supports reusable hash | |
| Status = BCryptOpenAlgorithmProvider( | |
| &AlgHandle, // Alg Handle pointer | |
| BCRYPT_SHA256_ALGORITHM, // Cryptographic Algorithm name (null terminated unicode string) | |
| NULL, // Provider name; if null, the default provider is loaded | |
| BCRYPT_HASH_REUSABLE_FLAG); // Flags; Loads a provider which supports reusable hash | |
| // Obtain the length of the hash | |
| Status = BCryptGetProperty( | |
| AlgHandle, // Handle to a CNG object | |
| BCRYPT_HASH_LENGTH, // Property name (null terminated unicode string) | |
| (PBYTE)&HashLength, // Address of the output buffer which recieves the property value | |
| sizeof(HashLength), // Size of the buffer in bytes | |
| &ResultLength, // Number of bytes that were copied into the buffer | |
| 0); // Flags | |
| // Allocate the hash buffer on the heap | |
| Hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, HashLength); | |
| if (Hash == NULL) | |
| { | |
| Status = STATUS_NO_MEMORY; | |
| } | |
| // Create a hash handle | |
| Status = BCryptCreateHash( | |
| AlgHandle, // Handle to an algorithm provider | |
| &HashHandle, // A pointer to a hash handle - can be a hash or hmac object | |
| NULL, // Pointer to the buffer that recieves the hash/hmac object | |
| 0, // Size of the buffer in bytes | |
| NULL, // A pointer to a key to use for the hash or MAC | |
| 0, // Size of the key in bytes | |
| 0); // Flags | |
| if (!NT_SUCCESS(Status)) | |
| { | |
| goto cleanup; | |
| } | |
| // | |
| // Hash the message(s) | |
| // More than one message can be hashed by calling BCryptHashData | |
| // | |
| Status = BCryptHashData( | |
| HashHandle, // Handle to the hash or MAC object | |
| (PBYTE)Data, // A pointer to a buffer that contains the data to hash | |
| DataSize, // Size of the buffer in bytes | |
| 0); // Flags | |
| if (!NT_SUCCESS(Status)) | |
| { | |
| goto cleanup; | |
| } | |
| // | |
| // Obtain the hash of the message(s) into the hash buffer | |
| // | |
| Status = BCryptFinishHash( | |
| HashHandle, // Handle to the hash or MAC object | |
| Hash, // A pointer to a buffer that receives the hash or MAC value | |
| HashLength, // Size of the buffer in bytes | |
| 0); // Flags | |
| if (!NT_SUCCESS(Status)) | |
| { | |
| goto cleanup; | |
| } | |
| memmove(OutHash, Hash, HashLength); | |
| Status = 0; | |
| cleanup: | |
| if (NULL != Hash) | |
| { | |
| HeapFree(GetProcessHeap(), 0, Hash); | |
| } | |
| if (NULL != HashHandle) | |
| { | |
| BCryptDestroyHash(HashHandle); // Handle to hash/MAC object which needs to be destroyed | |
| } | |
| if (NULL != AlgHandle) | |
| { | |
| BCryptCloseAlgorithmProvider( | |
| AlgHandle, // Handle to the algorithm provider which needs to be closed | |
| 0); // Flags | |
| } | |
| return NT_SUCCESS(Status); | |
| } | |
| // Arbitrary data, can be changed | |
| BYTE test[] = { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x90, 0x90 }; | |
| FEISTEL64_ROUND_DATA rounds[] = { {7, 44, 200, 29}, | |
| {14, 232, 21, 169}, | |
| {21, 255, 51, 128}, | |
| {28, 253, 78, 79}, | |
| {4, 119, 63, 70}, | |
| {11, 51, 194, 231}, | |
| {18, 21, 61, 212}, | |
| {25, 183, 13, 175}, | |
| {1, 248, 94, 21}, | |
| {8, 131, 55, 240} }; | |
| ULONG64 key = 0x1234567869694200; | |
| ULONG64 code_offset = 0x3000; | |
| int main() | |
| { | |
| PBYTE bin = (PBYTE)LoadLibraryA("clipc.dll"); | |
| if (!bin) { | |
| printf("ded\n"); | |
| return 1; | |
| } | |
| DWORD oldProt; | |
| HEAP_EXECUTE_CALL_ARGUMENT hx = { 0 }; | |
| ENCRYPTION_SEGMENT seg = { 0 }; | |
| hx.ulStructSize = sizeof(HEAP_EXECUTE_CALL_ARGUMENT); | |
| hx.ullKey = key; | |
| hx.ulRva = code_offset; | |
| hx.ulSize = sizeof(test); | |
| seg.ulStructSize = sizeof(ENCRYPTION_SEGMENT); | |
| seg.ullKey = key; | |
| seg.cBlocks = 1; | |
| seg.Blocks[0].RVA = code_offset; | |
| seg.Blocks[0].Length = sizeof(test); | |
| memcpy((void*)&hx.RoundData, (void*)&rounds, sizeof(rounds)); | |
| memcpy((void*)&seg.bRoundData, (void*)&rounds, sizeof(rounds)); | |
| if (!SHACompute((PBYTE)&hx + 0x20, sizeof(hx) - 0x20, hx.bHash)) { | |
| printf("Failed to hash segment"); | |
| return 1; | |
| } | |
| if (!SHACompute((PBYTE)&seg + 0x20, sizeof(seg) - 0x20, seg.bHash)) { | |
| printf("Failed to hash segment"); | |
| return 1; | |
| } | |
| // False decrypt | |
| ULONGLONG dc_params[] = { | |
| 1, | |
| (ULONG64)bin, | |
| (ULONG64)bin, | |
| 0x140000000, | |
| 0, | |
| 0 | |
| }; | |
| VirtualProtect(bin, 0x10000, PAGE_READWRITE, &oldProt); | |
| memcpy(bin, &seg, sizeof(seg)); | |
| VirtualProtect(bin, 0x10000, PAGE_EXECUTE_READ, &oldProt); | |
| NTSTATUS ret = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)0xB9, dc_params, sizeof(dc_params), 0); | |
| if (NT_ERROR(ret)) { | |
| printf("Failed decrypt! 0x%08x", ret); | |
| return 1; | |
| } | |
| // Re-encrypt | |
| ULONGLONG en_params[] = { | |
| 2, | |
| (ULONG64)bin, | |
| (ULONG64)bin, | |
| 0x140000000, | |
| 0, | |
| 0 | |
| }; | |
| VirtualProtect(bin, 0x10000, PAGE_READWRITE, &oldProt); | |
| memcpy(bin + code_offset, test, sizeof(test)); | |
| VirtualProtect(bin, 0x10000, PAGE_EXECUTE_READ, &oldProt); | |
| ret = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)0xB9, en_params, sizeof(en_params), 0); | |
| if (NT_ERROR(ret)) { | |
| printf("Failed encrypt! 0x%08x", ret); | |
| return 1; | |
| } | |
| // Heap execute | |
| ULONGLONG hxret; | |
| ULONGLONG hx_params[] = { | |
| 3, | |
| (ULONG64)bin, | |
| (ULONG64)&hxret, | |
| 6, | |
| 9, | |
| 4, | |
| 20 | |
| }; | |
| VirtualProtect(bin, 0x10000, PAGE_READWRITE, &oldProt); | |
| memcpy(bin, &hx, sizeof(hx)); | |
| VirtualProtect(bin, 0x10000, PAGE_EXECUTE_READ, &oldProt); | |
| ret = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)0xB9, hx_params, sizeof(hx_params), 0); | |
| if (NT_ERROR(ret)) { | |
| printf("Failed HX! 0x%08x", ret); | |
| return 1; | |
| } | |
| return 0; | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
testcontains shellcode to be executed, this example usesint 3so you can observe where heap-executed code ends up