Skip to content

Instantly share code, notes, and snippets.

@WitherOrNot
Last active February 6, 2026 10:37
Show Gist options
  • Select an option

  • Save WitherOrNot/c6fa56b943b09bdd8a23fe43cec6f4cc to your computer and use it in GitHub Desktop.

Select an option

Save WitherOrNot/c6fa56b943b09bdd8a23fe43cec6f4cc to your computer and use it in GitHub Desktop.
#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;
}
@WitherOrNot
Copy link
Author

test contains shellcode to be executed, this example uses int 3 so you can observe where heap-executed code ends up

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment