#include "oneapi/tbb/detail/_config.h"
#include "oneapi/tbb/detail/_assert.h"
#include "../tbb/assert_impl.h"
#if !__TBB_WIN8UI_SUPPORT && defined(_WIN32)
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE 1
#endif
#if !defined(_MSC_VER) || _MSC_VER>=1900
#define LOG_PRINT(s, n, format, ...) snprintf(s, n, format, __VA_ARGS__)
#else
#define LOG_PRINT(s, n, format, ...) _snprintf_s(s, n, _TRUNCATE, format, __VA_ARGS__)
#endif
#include <windows.h>
#include <new>
#include <stdio.h>
#include <string.h>
#include "function_replacement.h"
struct FunctionInfo {
const char* funcName;
const char* dllName;
};
namespace Log {
static const unsigned RECORDS_COUNT = 8 * 8;
static const unsigned RECORD_LENGTH = MAX_PATH;
static char *records[RECORDS_COUNT + 1];
static bool replacement_status = true;
static unsigned record_number = 0;
static void record(FunctionInfo functionInfo, const char * opcodeString, bool status) {
__TBB_ASSERT(functionInfo.dllName, "Empty DLL name value");
__TBB_ASSERT(functionInfo.funcName, "Empty function name value");
__TBB_ASSERT(opcodeString, "Empty opcode");
__TBB_ASSERT(record_number <= RECORDS_COUNT, "Incorrect record number");
replacement_status &= status;
if (record_number == RECORDS_COUNT) {
LOG_PRINT(records[RECORDS_COUNT - 1], RECORD_LENGTH, "%s", "Log was truncated.");
return;
}
char* entry = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, RECORD_LENGTH);
__TBB_ASSERT(entry, "Invalid memory was returned");
LOG_PRINT(entry, RECORD_LENGTH, "%s: %s (%s), byte pattern: <%s>",
status ? "Success" : "Fail", functionInfo.funcName, functionInfo.dllName, opcodeString);
records[record_number++] = entry;
}
};
inline UINT_PTR Ptr2Addrint(LPVOID ptr)
{
Int2Ptr i2p;
i2p.lpv = ptr;
return i2p.uip;
}
inline LPVOID Addrint2Ptr(UINT_PTR ptr)
{
Int2Ptr i2p;
i2p.uip = ptr;
return i2p.lpv;
}
inline bool IsInDistance(UINT_PTR addr1, UINT_PTR addr2, __int64 dist)
{
__int64 diff = addr1>addr2 ? addr1-addr2 : addr2-addr1;
return diff<dist;
}
class MemoryProvider {
private:
struct MemoryBuffer {
UINT_PTR m_base; UINT_PTR m_next; DWORD m_size;
MemoryBuffer() : m_base(0), m_next(0), m_size(0) {}
MemoryBuffer(void *base, DWORD size)
{
m_base = Ptr2Addrint(base);
m_next = m_base;
m_size = size;
}
};
MemoryBuffer *CreateBuffer(UINT_PTR addr)
{
if (m_lastBuffer - m_pages == MAX_NUM_BUFFERS)
return 0;
void *newAddr = Addrint2Ptr(addr);
MEMORY_BASIC_INFORMATION memInfo;
if (VirtualQuery(newAddr, &memInfo, sizeof(memInfo)) != sizeof(memInfo))
return 0;
for(;;) {
newAddr = Addrint2Ptr( (Ptr2Addrint(memInfo.BaseAddress) + memInfo.RegionSize + m_allocSize) & ~(UINT_PTR)(m_allocSize-1) );
if (!IsInDistance(addr, Ptr2Addrint(newAddr), MAX_DISTANCE))
break;
if (VirtualQuery(newAddr, &memInfo, sizeof(memInfo)) != sizeof(memInfo))
break;
if (memInfo.State == MEM_FREE && memInfo.RegionSize >= m_allocSize)
{
void *newPage = VirtualAlloc(newAddr, m_allocSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (!newPage)
break;
MemoryBuffer *pBuff = new (m_lastBuffer) MemoryBuffer(newPage, m_allocSize);
++m_lastBuffer;
return pBuff;
}
}
return 0;
}
public:
MemoryProvider()
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
m_allocSize = sysInfo.dwAllocationGranularity;
m_lastBuffer = &m_pages[0];
}
~MemoryProvider()
{
}
UINT_PTR GetLocation(UINT_PTR addr)
{
MemoryBuffer *pBuff = m_pages;
for (; pBuff<m_lastBuffer && IsInDistance(pBuff->m_next, addr, MAX_DISTANCE); ++pBuff)
{
if (pBuff->m_next < pBuff->m_base + pBuff->m_size)
{
UINT_PTR loc = pBuff->m_next;
pBuff->m_next += MAX_PROBE_SIZE;
return loc;
}
}
pBuff = CreateBuffer(addr);
if(!pBuff)
return 0;
UINT_PTR loc = pBuff->m_next;
pBuff->m_next += MAX_PROBE_SIZE;
return loc;
}
private:
MemoryBuffer m_pages[MAX_NUM_BUFFERS];
MemoryBuffer *m_lastBuffer;
DWORD m_allocSize;
};
static MemoryProvider memProvider;
size_t compareStrings( const char *str1, const char *str2 )
{
for (size_t i=0; str1[i]!=0; i++){
if( str1[i]!='*' && str1[i]!='#' && str1[i]!=str2[i] ) return 0;
}
return 1;
}
static UINT CheckOpcodes( const char ** opcodes, void *inpAddr, bool abortOnError, const FunctionInfo* functionInfo = NULL)
{
static size_t opcodesStringsCount = 0;
static size_t maxOpcodesLength = 0;
static size_t opcodes_pointer = (size_t)opcodes;
char opcodeString[2*MAX_PATTERN_SIZE+1];
size_t i;
size_t result = 0;
if( !opcodesStringsCount || opcodes_pointer != (size_t)opcodes ){
while( *(opcodes + opcodesStringsCount)!= NULL ){
if( (i=strlen(*(opcodes + opcodesStringsCount))) > maxOpcodesLength )
maxOpcodesLength = i;
opcodesStringsCount++;
}
opcodes_pointer = (size_t)opcodes;
__TBB_ASSERT( maxOpcodesLength/2 <= MAX_PATTERN_SIZE, "Pattern exceeded the limit of 28 opcodes/56 symbols" );
}
for( i=0; i<maxOpcodesLength/2 && i<MAX_PATTERN_SIZE; ++i ){
sprintf( opcodeString + 2*i, "%.2X", *((unsigned char*)inpAddr+i) );
}
opcodeString[2*i] = 0;
for( UINT idx=0; idx<opcodesStringsCount; ++idx ){
result = compareStrings( opcodes[idx],opcodeString );
if( result ) {
if (functionInfo) {
Log::record(*functionInfo, opcodeString, true);
}
return idx + 1; }
}
if (functionInfo) {
Log::record(*functionInfo, opcodeString, false);
}
if (abortOnError) {
__TBB_ASSERT_RELEASE( false, "CheckOpcodes failed" );
}
return 0;
}
static void CorrectOffset( UINT_PTR address, const char* pattern, UINT distance )
{
const char* pos = strstr(pattern, "#*******");
if( pos ) {
address += (pos - pattern)/2; UINT value;
memcpy(&value, Addrint2Ptr(address), sizeof(value));
value += distance;
memcpy(Addrint2Ptr(address), &value, sizeof(value));
}
}
static DWORD InsertTrampoline32(void *inpAddr, void *targetAddr, const char* pattern, void** storedAddr)
{
size_t bytesToMove = SIZE_OF_RELJUMP;
UINT_PTR srcAddr = Ptr2Addrint(inpAddr);
UINT_PTR tgtAddr = Ptr2Addrint(targetAddr);
if (!IsInDistance(srcAddr, tgtAddr, MAX_DISTANCE))
return 0;
UINT_PTR offset;
UINT offset32;
UCHAR *codePtr = (UCHAR *)inpAddr;
if ( storedAddr ){ bytesToMove = strlen(pattern)/2-1; __TBB_ASSERT_RELEASE( bytesToMove >= SIZE_OF_RELJUMP, "Incorrect bytecode pattern?" );
UINT_PTR trampAddr = memProvider.GetLocation(srcAddr);
if (!trampAddr)
return 0;
*storedAddr = Addrint2Ptr(trampAddr);
DWORD pageFlags = PAGE_EXECUTE_READWRITE;
if (!VirtualProtect(*storedAddr, MAX_PROBE_SIZE, pageFlags, &pageFlags)) return 0;
memcpy(*storedAddr, codePtr, bytesToMove);
offset = srcAddr - trampAddr;
offset32 = (UINT)(offset & 0xFFFFFFFF);
CorrectOffset( trampAddr, pattern, offset32 );
offset32 -= SIZE_OF_RELJUMP;
*(UCHAR*)(trampAddr+bytesToMove) = 0xE9;
memcpy((UCHAR*)(trampAddr+bytesToMove+1), &offset32, sizeof(offset32));
}
offset = tgtAddr - srcAddr - SIZE_OF_RELJUMP;
offset32 = (UINT)(offset & 0xFFFFFFFF);
*codePtr = 0xE9;
memcpy(codePtr+1, &offset32, sizeof(offset32));
for( unsigned i=SIZE_OF_RELJUMP; i<bytesToMove; i++ ){
*(codePtr+i) = 0x90;
}
return SIZE_OF_RELJUMP;
}
static DWORD InsertTrampoline64(void *inpAddr, void *targetAddr, const char* pattern, void** storedAddr)
{
size_t bytesToMove = SIZE_OF_INDJUMP;
UINT_PTR srcAddr = Ptr2Addrint(inpAddr);
UINT_PTR tgtAddr = Ptr2Addrint(targetAddr);
UINT_PTR location = memProvider.GetLocation(srcAddr);
if (!location)
return 0;
UINT_PTR offset;
UINT offset32;
UCHAR *codePtr = (UCHAR *)inpAddr;
UINT_PTR *locPtr = (UINT_PTR *)Addrint2Ptr(location);
*locPtr = tgtAddr;
if ( storedAddr ){ bytesToMove = strlen(pattern)/2-1; __TBB_ASSERT_RELEASE( bytesToMove >= SIZE_OF_INDJUMP, "Incorrect bytecode pattern?" );
UINT_PTR trampAddr = memProvider.GetLocation(srcAddr);
if (!trampAddr)
return 0;
*storedAddr = Addrint2Ptr(trampAddr);
DWORD pageFlags = PAGE_EXECUTE_READWRITE;
if (!VirtualProtect(*storedAddr, MAX_PROBE_SIZE, pageFlags, &pageFlags)) return 0;
memcpy(*storedAddr, codePtr, bytesToMove);
offset = srcAddr - trampAddr;
offset32 = (UINT)(offset & 0xFFFFFFFF);
CorrectOffset( trampAddr, pattern, offset32 );
offset32 -= SIZE_OF_RELJUMP;
*(UCHAR*)(trampAddr+bytesToMove) = 0xE9;
memcpy((UCHAR*)(trampAddr+bytesToMove+1), &offset32, sizeof(offset32));
}
offset = location - srcAddr - SIZE_OF_INDJUMP;
offset32 = (UINT)(offset & 0xFFFFFFFF);
*(codePtr) = 0xFF;
*(codePtr+1) = 0x25;
memcpy(codePtr+2, &offset32, sizeof(offset32));
for( unsigned i=SIZE_OF_INDJUMP; i<bytesToMove; i++ ){
*(codePtr+i) = 0x90;
}
return SIZE_OF_INDJUMP;
}
static bool InsertTrampoline(void *inpAddr, void *targetAddr, const char ** opcodes, void** origFunc)
{
DWORD probeSize;
DWORD origProt = 0;
if (!VirtualProtect(inpAddr, MAX_PROBE_SIZE, PAGE_EXECUTE_WRITECOPY, &origProt))
return FALSE;
const char* pattern = NULL;
if ( origFunc ){ UCHAR * const codePtr = (UCHAR *)inpAddr;
if ( *codePtr == 0xE9 ){ unsigned offsetInJmp = *(unsigned*)(codePtr + 1);
*origFunc = (void*)(Ptr2Addrint(inpAddr) + offsetInJmp + SIZE_OF_RELJUMP);
origFunc = NULL; } else {
UINT opcodeIdx = CheckOpcodes( opcodes, inpAddr, true );
__TBB_ASSERT( opcodeIdx > 0, "abortOnError ignored in CheckOpcodes?" );
pattern = opcodes[opcodeIdx-1]; }
}
probeSize = InsertTrampoline32(inpAddr, targetAddr, pattern, origFunc);
if (!probeSize)
probeSize = InsertTrampoline64(inpAddr, targetAddr, pattern, origFunc);
VirtualProtect(inpAddr, MAX_PROBE_SIZE, origProt, &origProt);
if (!probeSize)
return FALSE;
FlushInstructionCache(GetCurrentProcess(), inpAddr, probeSize);
FlushInstructionCache(GetCurrentProcess(), origFunc, probeSize);
return TRUE;
}
FRR_TYPE ReplaceFunctionA(const char *dllName, const char *funcName, FUNCPTR newFunc, const char ** opcodes, FUNCPTR* origFunc)
{
static char cachedName[MAX_PATH+1];
static HMODULE cachedHM = 0;
if (!dllName || !*dllName)
return FRR_NODLL;
if (!cachedHM || strncmp(dllName, cachedName, MAX_PATH) != 0)
{
HMODULE hModule = GetModuleHandleA(dllName);
if (hModule == 0)
{
cachedHM = 0;
return FRR_NODLL;
}
cachedHM = hModule;
strncpy(cachedName, dllName, MAX_PATH);
}
FARPROC inpFunc = GetProcAddress(cachedHM, funcName);
if (inpFunc == 0)
{
return FRR_NOFUNC;
}
if (!InsertTrampoline((void*)inpFunc, (void*)newFunc, opcodes, (void**)origFunc)){
return FRR_FAILED;
}
return FRR_OK;
}
FRR_TYPE ReplaceFunctionW(const wchar_t *dllName, const char *funcName, FUNCPTR newFunc, const char ** opcodes, FUNCPTR* origFunc)
{
static wchar_t cachedName[MAX_PATH+1];
static HMODULE cachedHM = 0;
if (!dllName || !*dllName)
return FRR_NODLL;
if (!cachedHM || wcsncmp(dllName, cachedName, MAX_PATH) != 0)
{
HMODULE hModule = GetModuleHandleW(dllName);
if (hModule == 0)
{
cachedHM = 0;
return FRR_NODLL;
}
cachedHM = hModule;
wcsncpy(cachedName, dllName, MAX_PATH);
}
FARPROC inpFunc = GetProcAddress(cachedHM, funcName);
if (inpFunc == 0)
{
return FRR_NOFUNC;
}
if (!InsertTrampoline((void*)inpFunc, (void*)newFunc, opcodes, (void**)origFunc)){
return FRR_FAILED;
}
return FRR_OK;
}
bool IsPrologueKnown(const char* dllName, const char *funcName, const char **opcodes, HMODULE module)
{
FARPROC inpFunc = GetProcAddress(module, funcName);
FunctionInfo functionInfo = { funcName, dllName };
if (!inpFunc) {
Log::record(functionInfo, "unknown", false);
return false;
}
return CheckOpcodes( opcodes, (void*)inpFunc, false, &functionInfo) != 0;
}
extern "C" __declspec(dllexport) int TBB_malloc_replacement_log(char *** function_replacement_log_ptr)
{
if (function_replacement_log_ptr != NULL) {
*function_replacement_log_ptr = Log::records;
}
return Log::replacement_status && Log::records[0] != NULL ? 0 : -1;
}
#endif