void* AllocateFunctionStub(void* origin, void* function, int type) { static void* g_currentStub = nullptr; if (!g_currentStub) { ULONG_PTR minAddr; ULONG_PTR maxAddr; SYSTEM_INFO si; GetSystemInfo(&si); minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; if ((ULONG_PTR)origin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)origin - MAX_MEMORY_RANGE) minAddr = (ULONG_PTR)origin - MAX_MEMORY_RANGE; if (maxAddr > (ULONG_PTR)origin + MAX_MEMORY_RANGE) maxAddr = (ULONG_PTR)origin + MAX_MEMORY_RANGE; LPVOID pAlloc = origin; while ((ULONG_PTR)pAlloc >= minAddr) { pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); if (pAlloc == NULL) break; g_currentStub = VirtualAlloc(pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (g_currentStub != NULL) break; } } if (!g_currentStub) return nullptr; char* code = (char*)g_currentStub; *(uint8_t*)code = 0x48; *(uint8_t*)(code + 1) = 0xb8 | type; *(uint64_t*)(code + 2) = (uint64_t)function; *(uint16_t*)(code + 10) = 0xE0FF | (type << 8); *(uint64_t*)(code + 12) = 0xCCCCCCCCCCCCCCCC; g_currentStub = (void*)((uint64_t)g_currentStub + 20); return code; }
//------------------------------------------------------------------------- static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) { PMEMORY_BLOCK pBlock; #ifdef _M_X64 ULONG_PTR minAddr; ULONG_PTR maxAddr; SYSTEM_INFO si; GetSystemInfo(&si); minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; // pOrigin ± 512MB if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE) minAddr = max(minAddr, (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE); maxAddr = min(maxAddr, (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE); // Make room for MEMORY_BLOCK_SIZE bytes. maxAddr -= MEMORY_BLOCK_SIZE - 1; #endif // Look the registered blocks for a reachable one. for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) { #ifdef _M_X64 // Ignore the blocks too far. if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) continue; #endif // The block has at least one unused slot. if (pBlock->pFree != NULL) return pBlock; } #ifdef _M_X64 // Alloc a new block above if not found. { LPVOID pAlloc = pOrigin; while ((ULONG_PTR)pAlloc >= minAddr) { pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); if (pAlloc == NULL) break; pBlock = (PMEMORY_BLOCK)VirtualAlloc( pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (pBlock != NULL) break; } } // Alloc a new block below if not found. if (pBlock == NULL) { LPVOID pAlloc = pOrigin; while ((ULONG_PTR)pAlloc <= maxAddr) { pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); if (pAlloc == NULL) break; pBlock = (PMEMORY_BLOCK)VirtualAlloc( pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (pBlock != NULL) break; } } #else // In x86 mode, a memory block can be placed anywhere. pBlock = (PMEMORY_BLOCK)VirtualAlloc( NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); #endif if (pBlock != NULL) { // Build a linked list of all the slots. PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; pBlock->pFree = NULL; pBlock->usedCount = 0; do { pSlot->pNext = pBlock->pFree; pBlock->pFree = pSlot; pSlot++; } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); pBlock->pNext = g_pMemoryBlocks; g_pMemoryBlocks = pBlock; } return pBlock; }