mirror of
https://github.com/phra/PEzor
synced 2026-06-08 16:45:36 +00:00
336 lines
11 KiB
C++
336 lines
11 KiB
C++
#include "fluctuate.hpp"
|
|
|
|
unsigned char originalBytes[16] = { 0 };
|
|
unsigned int originalProtection = 0;
|
|
unsigned int XOR_KEY = 0;
|
|
|
|
#ifdef _DEBUG_
|
|
// https://gist.github.com/ccbrown/9722406
|
|
void DumpHex(const void* data, size_t size) {
|
|
char ascii[17];
|
|
size_t i, j;
|
|
ascii[16] = '\0';
|
|
for (i = 0; i < size; ++i) {
|
|
wprintf(L"%02X ", ((unsigned char*)data)[i]);
|
|
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
|
|
ascii[i % 16] = ((unsigned char*)data)[i];
|
|
} else {
|
|
ascii[i % 16] = '.';
|
|
}
|
|
if ((i+1) % 8 == 0 || i+1 == size) {
|
|
wprintf(L" ");
|
|
if ((i+1) % 16 == 0) {
|
|
wprintf(L"| %s \n", ascii);
|
|
} else if (i+1 == size) {
|
|
ascii[(i+1) % 16] = '\0';
|
|
if ((i+1) % 16 <= 8) {
|
|
wprintf(L" ");
|
|
}
|
|
for (j = (i+1) % 16; j < 16; ++j) {
|
|
wprintf(L" ");
|
|
}
|
|
wprintf(L"| %s \n", ascii);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void print_protection(void* address, DWORD protections) {
|
|
if (protections & PAGE_READONLY)
|
|
wprintf(L"print_protection: 0x%p is PAGE_READONLY\n", address);
|
|
if (protections & PAGE_READWRITE)
|
|
wprintf(L"print_protection: 0x%p is PAGE_READWRITE\n", address);
|
|
if (protections & PAGE_NOACCESS)
|
|
wprintf(L"print_protection: 0x%p is PAGE_NOACCESS\n", address);
|
|
if (protections & PAGE_WRITECOPY)
|
|
wprintf(L"print_protection: 0x%p is PAGE_WRITECOPY\n", address);
|
|
if (protections & PAGE_EXECUTE)
|
|
wprintf(L"print_protection: 0x%p is PAGE_EXECUTE\n", address);
|
|
if (protections & PAGE_EXECUTE_READ)
|
|
wprintf(L"print_protection: 0x%p is PAGE_EXECUTE_READ\n", address);
|
|
if (protections & PAGE_EXECUTE_READWRITE)
|
|
wprintf(L"print_protection: 0x%p is PAGE_EXECUTE_READWRITE\n", address);
|
|
if (protections & PAGE_EXECUTE_WRITECOPY)
|
|
wprintf(L"print_protection: 0x%p is PAGE_EXECUTE_WRITECOPY\n", address);
|
|
if (protections & PAGE_GUARD)
|
|
wprintf(L"print_protection: 0x%p is PAGE_GUARD\n", address);
|
|
if (protections & PAGE_NOCACHE)
|
|
wprintf(L"print_protection: 0x%p is PAGE_NOCACHE\n", address);
|
|
}
|
|
|
|
void print_type(void* address, DWORD type) {
|
|
if (type & MEM_IMAGE)
|
|
wprintf(L"print_type: 0x%p is MEM_IMAGE\n", address);
|
|
if (type & MEM_MAPPED)
|
|
wprintf(L"print_type: 0x%p is MEM_MAPPED\n", address);
|
|
if (type & MEM_PRIVATE)
|
|
wprintf(L"print_type: 0x%p is MEM_PRIVATE\n", address);
|
|
}
|
|
#endif
|
|
|
|
unsigned int generate_random_int() {
|
|
std::srand(std::time(nullptr));
|
|
return std::rand() | std::rand() << 16;
|
|
}
|
|
|
|
void xor32(void* address, unsigned int size, unsigned int key) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"xor32: before xor 0x%p:\n", address);
|
|
fflush(stdout);
|
|
DumpHex(address, 64);
|
|
#endif
|
|
for (struct { unsigned char* p; unsigned int i; } s = { (unsigned char*)address, 0 }; s.p < (unsigned char*)address + size; s.p++, s.i++) {
|
|
unsigned char currentKey = ((unsigned char*)&key)[s.i % 4];
|
|
*(s.p) ^= currentKey;
|
|
}
|
|
|
|
#ifdef _DEBUG_
|
|
wprintf(L"xor32: after xor 0x%p:\n", address);
|
|
fflush(stdout);
|
|
DumpHex(address, 64);
|
|
#endif
|
|
}
|
|
|
|
BOOL is_shellcode_thread(void* address) {
|
|
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
|
if (VirtualQuery(address, &mbi, sizeof(mbi))) {
|
|
#ifdef _DEBUG_
|
|
print_protection(address, mbi.Protect);
|
|
print_type(address, mbi.Type);
|
|
#endif
|
|
return mbi.Type == MEM_PRIVATE || mbi.Type == MEM_MAPPED || mbi.Type == MEM_IMAGE;
|
|
} else {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"get_allocation_base_from_pointer: cannot get protection for 0x%p\n", address);
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
}
|
|
|
|
void* get_allocation_base_from_pointer(void* address) {
|
|
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
|
if (!VirtualQuery(address, &mbi, sizeof(mbi))) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"get_allocation_base_from_pointer: cannot get allocation base for 0x%p\n", address);
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
|
|
return mbi.AllocationBase;
|
|
}
|
|
|
|
SIZE_T get_region_size_from_pointer(void* address) {
|
|
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
|
if (!VirtualQuery(address, &mbi, sizeof(mbi))) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"get_region_size_from_pointer: cannot get region size for 0x%p\n", address);
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
return (unsigned char*)mbi.BaseAddress - (unsigned char*)mbi.AllocationBase + mbi.RegionSize;
|
|
}
|
|
|
|
unsigned int get_protection_from_pointer(void* address) {
|
|
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
|
if (!VirtualQuery(address, &mbi, sizeof(mbi))) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"get_region_size_from_pointer: cannot get protection for 0x%p\n", address);
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
|
|
return mbi.Protect;
|
|
}
|
|
|
|
void xor_region_and_change_protection(void* address, unsigned int key, DWORD newProtection) {
|
|
DWORD oldProt = 0;
|
|
void* allocation_base = get_allocation_base_from_pointer(address);
|
|
SIZE_T region_size = get_region_size_from_pointer(address);
|
|
|
|
#ifdef _DEBUG_
|
|
wprintf(L"xor_region_and_change_protection: xoring 0x%p (base: 0x%p, length: %d) with key 0x%x (new protection: %d)\n", address, allocation_base, region_size, key, newProtection);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
if (!VirtualProtect(allocation_base, region_size, PAGE_READWRITE, &oldProt)) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"xor_region_and_change_protection: cannot change protection (PAGE_READWRITE) for 0x%p, %d (last error: 0x%x)\n", allocation_base, region_size, GetLastError());
|
|
fflush(stdout);
|
|
#endif
|
|
if (!VirtualProtect(allocation_base, region_size, PAGE_EXECUTE_WRITECOPY, &oldProt)) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"xor_region_and_change_protection: cannot change protection (PAGE_EXECUTE_WRITECOPY) for 0x%p, %d (last error: 0x%x)\n", allocation_base, region_size, GetLastError());
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
ExitProcess(-1);
|
|
} else if (newProtection == PAGE_READWRITE) {
|
|
newProtection = PAGE_EXECUTE_WRITECOPY;
|
|
}
|
|
}
|
|
|
|
FlushInstructionCache((HANDLE)-1, allocation_base, region_size);
|
|
xor32(allocation_base, region_size, key);
|
|
|
|
if (!VirtualProtect(allocation_base, region_size, newProtection, &oldProt)) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"xor_region_and_change_protection: cannot change protection for 0x%p, %d (last error: 0x%x)\n", allocation_base, region_size, GetLastError());
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
|
|
FlushInstructionCache((HANDLE)-1, allocation_base, region_size);
|
|
}
|
|
|
|
void inline_hook_function(BOOL enable, char* addressToHook, void* jumpAddress) {
|
|
#ifdef _WIN64
|
|
unsigned char trampoline[] = {
|
|
0x49, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r10, addr
|
|
0x41, 0xFF, 0xE2 // jmp r10
|
|
};
|
|
|
|
memcpy(&trampoline[2], &jumpAddress, sizeof(jumpAddress));
|
|
#else
|
|
unsigned char trampoline[] = {
|
|
0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, addr
|
|
0xFF, 0xE0 // jmp eax
|
|
};
|
|
|
|
memcpy(&trampoline[1], &jumpAddress, sizeof(jumpAddress));
|
|
#endif
|
|
|
|
DWORD trampolineLength = sizeof(trampoline);
|
|
DWORD oldProt = 0;
|
|
|
|
if (VirtualProtect(addressToHook, trampolineLength, PAGE_EXECUTE_READWRITE, &oldProt)) {
|
|
if (enable) {
|
|
memcpy(originalBytes, addressToHook, trampolineLength);
|
|
memcpy(addressToHook, trampoline, trampolineLength);
|
|
} else {
|
|
memcpy(addressToHook, originalBytes, trampolineLength);
|
|
}
|
|
} else {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"inline_hook_function: cannot change protection for 0x%p\n", addressToHook);
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
|
|
FlushInstructionCache((HANDLE)-1, addressToHook, trampolineLength);
|
|
|
|
if (!VirtualProtect(addressToHook, trampolineLength, oldProt, &oldProt)) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"inline_hook_function: cannot change protection for 0x%p\n", addressToHook);
|
|
fflush(stdout);
|
|
#endif
|
|
ExitProcess(-1);
|
|
}
|
|
}
|
|
|
|
#ifdef FLUCTUATE_NA
|
|
LONG VEHHandler(PEXCEPTION_POINTERS pExceptInfo) {
|
|
#ifdef _WIN64
|
|
void* caller = (void*)pExceptInfo->ContextRecord->Rip;
|
|
#else
|
|
void* caller = (void*)pExceptInfo->ContextRecord->Eip;
|
|
#endif
|
|
if (pExceptInfo->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION /*0xc0000005*/) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"VEHHandler: STATUS_ACCESS_VIOLATION at 0x%p\n", caller);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
if (is_shellcode_thread(caller)) {
|
|
#ifdef _DEBUG_
|
|
puts("VEHHandler: restoring original protection");
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
xor_region_and_change_protection(caller, XOR_KEY, originalProtection);
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG_
|
|
wprintf(L"unhandled exception: 0x%x\n", pExceptInfo->ExceptionRecord->ExceptionCode);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
#endif
|
|
|
|
void hook_sleep(BOOL enable);
|
|
|
|
void my_sleep(DWORD dwMilliseconds) {
|
|
// const LPVOID caller = _ReturnAddress();
|
|
void* caller = __builtin_extract_return_addr(__builtin_return_address(0));
|
|
// PULONG_PTR overwrite = (PULONG_PTR)_AddressOfReturnAddress();
|
|
//unsigned int* overwrite = *((unsigned int**)__builtin_frame_address(0)) + 1;
|
|
|
|
#ifdef _DEBUG_
|
|
wprintf(L"my_sleep: called with %d milliseconds by 0x%p\n", dwMilliseconds, caller);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
if (is_shellcode_thread(caller)) {
|
|
#ifdef _DEBUG_
|
|
wprintf(L"my_sleep: caller 0x%p is from shellcode\n", caller);
|
|
fflush(stdout);
|
|
#endif
|
|
originalProtection = get_protection_from_pointer(caller);
|
|
#ifdef FLUCTUATE_RW
|
|
xor_region_and_change_protection(caller, XOR_KEY, PAGE_READWRITE);
|
|
#elif FLUCTUATE_NA
|
|
xor_region_and_change_protection(caller, XOR_KEY, PAGE_NOACCESS);
|
|
#endif
|
|
}
|
|
|
|
hook_sleep(FALSE);
|
|
|
|
#ifdef _DEBUG_
|
|
wprintf(L"my_sleep: sleeping for %d milliseconds\n", dwMilliseconds);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
Sleep(dwMilliseconds);
|
|
|
|
if (is_shellcode_thread(caller)) {
|
|
#ifdef FLUCTUATE_RW
|
|
xor_region_and_change_protection(caller, XOR_KEY, originalProtection);
|
|
#endif
|
|
}
|
|
|
|
hook_sleep(TRUE);
|
|
}
|
|
|
|
void hook_sleep(BOOL enable) {
|
|
inline_hook_function(enable, (char*)&Sleep, (void*)&my_sleep);
|
|
}
|
|
|
|
void register_vectored_handler() {
|
|
#ifdef FLUCTUATE_NA
|
|
#ifdef _DEBUG_
|
|
puts("register_vectored_handler: adding vectored exception handler");
|
|
#endif
|
|
AddVectoredExceptionHandler(1, &VEHHandler);
|
|
#endif
|
|
}
|
|
|
|
void fluctuate() {
|
|
XOR_KEY = generate_random_int();
|
|
#ifdef FLUCTUATE_NA
|
|
register_vectored_handler();
|
|
#endif
|
|
#ifdef _DEBUG_
|
|
puts("fluctuate: hooking Sleep");
|
|
#endif
|
|
hook_sleep(TRUE);
|
|
}
|