Raised This Month: $12 Target: $400
 3% 

Fake Custom Entity (win32)


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
hzqst
Senior Member
Join Date: Jul 2008
Old 04-20-2015 , 01:24   Fake Custom Entity (win32)
Reply With Quote #1

First of all, i'm not a English native speaker so..just don't focous on my bad expression.

MS's Detours library is used.I'm not familiar with linux so no linux version here, but it's easy to make your own with IDA and some hook technique.

Why call it "fake" custom entity? because this kind of custom entity have no custom private data, you can just created an entity already defined in your gamedll, with a custom classname, that can be recognized by engine when loading level and parsing .bsp entity lump.
(Creating an entity with custom private data needs gamedll code like HLSDK\dlls\)

which means you need to read KeyValues and save them in entvars_t *pev rather than in private data m_xxxx.
you can redirect your custom entity to "info_target", and hook info_target 's Spawn & KeyValues & ... using hamsandwich or fakemeta in AMXX, it's a simple trick but very useful.

Here are the relative code:

engine.h

PHP Code:
#include <com_model.h>

typedef void (*ENTITYINIT)(entvars_t *pev);

typedef struct
{
    
char *(*ED_ParseEdict)(char *, edict_t *);
    
ENTITYINIT (*GetEntityInit)(char *);
}
hook_funcs_t;

extern hook_funcs_t gHookFuncs;
ENTITYINIT GetEntityInit(char *pClassName);
void Sys_Error(const char *error); 
engine.cpp

PHP Code:

DWORD g_dwEngineBase 
0;
DWORD g_dwEngineSize 0;

hook_funcs_t gHookFuncs;

void Sys_Error(const char *error)
{
    
MessageBox(NULLerror"Error"MB_ICONERROR);
    exit(
0);
}

#define SIG_NOT_FOUND(name) Sys_Error(UTIL_VarArgs("Unable to find: %s", name));

void Engine_InstallHook(void)
{
    
DWORD addr;
    
HMODULE hEngine;
    
int swds 1;
    
    
hEngine GetModuleHandle("swds.dll");
    if(!
hEngine || hEngine == INVALID_HANDLE_VALUE)
    {
        
swds 0;
        
hEngine GetModuleHandle("hw.dll");
    }
    if(!
hEngine || hEngine == INVALID_HANDLE_VALUE)
    {
        
g_dwEngineBase 0x1D01000;
        
g_dwEngineSize 0x1000000;
    }
    else
    {
        
g_dwEngineBase MH_GetModuleBase(hEngine);
        
g_dwEngineSize MH_GetModuleSize(hEngine);
    }

#define ED_PARSEEDICT_SIG "\xA1\x2A\x2A\x2A\x2A\x81\xEC\x24\x03\x00\x00\x53\x55\x8B\xAC\x24\x34\x03\x00\x00\x33\xDB\x56\x3B\xE8\x57"
#define ED_PARSEEDICT_SIG_NEW "\x55\x8B\xEC\x81\xEC\x10\x01\x00\x00\xA1\x2A\x2A\x2A\x2A\x56\x8B\x75\x0C\x57\x3B\xF0\x74\x16"

    
gHookFuncs.ED_ParseEdict = (char *(*)(char *, edict_t *))MH_SIGFind(g_dwEngineBaseg_dwEngineSizeED_PARSEEDICT_SIGsizeof(ED_PARSEEDICT_SIG)-1);
    if(!
gHookFuncs.ED_ParseEdict)
        
gHookFuncs.ED_ParseEdict = (char *(*)(char *, edict_t *))MH_SIGFind(g_dwEngineBaseg_dwEngineSizeED_PARSEEDICT_SIG_NEWsizeof(ED_PARSEEDICT_SIG_NEW)-1);
    if(!
gHookFuncs.ED_ParseEdict)
        
SIG_NOT_FOUND("ED_ParseEdict");

//E8 2A 2A 2A 2A                                      call    GetEntityInit
//83 C4 04                                            add     esp, 4
//85 C0                                               test    eax, eax
#define GETENTITYINIT_SIG "\xE8\x2A\x2A\x2A\x2A\x83\xC4\x04\x85\xC0"

//E8 D9 E7 03 00                                      call    GetEntityInit
//83 C4 10                                            add     esp, 10h
//3B C3                                               cmp     eax, ebx
#define GETENTITYINIT_SIG2 "\xE8\x2A\x2A\x2A\x2A\x83\xC4\x10\x3B\xC3"

    
addr MH_SIGFind((DWORD)gHookFuncs.ED_ParseEdict0x100GETENTITYINIT_SIGsizeof(GETENTITYINIT_SIG)-1);
    if(!
addr)
        
addr MH_SIGFind((DWORD)gHookFuncs.ED_ParseEdict0x100GETENTITYINIT_SIG2sizeof(GETENTITYINIT_SIG2)-1);
    if(!
addr)
        
SIG_NOT_FOUND("GetEntityInit");
    
gHookFuncs.GetEntityInit = (ENTITYINIT (*)(char *))GetCallAddress(addr);

    
MH_InlineHook((void *)gHookFuncs.GetEntityInitGetEntityInit, (void *&)gHookFuncs.GetEntityInit);

    
SERVER_PRINT("Engine hook installed.\n");

hook.h

PHP Code:
#pragma once

#pragma pack(push, 1)

struct tagIATDATA
{
    
void *pAPIInfoAddr;
};

struct tagCLASS
{
    
DWORD *pVMT;
};

struct tagVTABLEDATA
{
    
tagCLASS *pInstance;
    
void *pVFTInfoAddr;
};

#pragma pack(pop)

typedef struct hook_s
{
    
void *pOldFuncAddr;
    
void *pNewFuncAddr;
    
void *pClass;
    
int iTableIndex;
    
int iFuncIndex;
    
HMODULE hModule;
    
LPCWSTR  pszModuleName;
    
LPCWSTR  pszFuncName;
    
struct hook_s *pNext;
    
void *pInfo;
}
hook_t;

hook_t *MH_NewHook(void);
void MH_FreeHook(hook_t *pHook);
void MH_FreeAllHook(void);
BOOL MH_UnHook(hook_t *pHook);
hook_t *MH_InlineHook(void *pOldFuncAddrvoid *pNewFuncAddrvoid *&pCallBackFuncAddr);
hook_t *MH_VFTHook(void *pClassint iTableIndexint iFuncIndexvoid *pNewFuncAddrvoid *&pCallBackFuncAddr);
DWORD MH_WriteMemory(void *pAddressBYTE *pDataDWORD dwDataSize);
DWORD MH_ReadMemory(void *pAddressBYTE *pDataDWORD dwDataSize);
DWORD MH_GetModuleBase(HMODULE hModule);
DWORD MH_GetModuleSize(HMODULE hModule);
DWORD MH_SIGFind(DWORD dwStartAddrDWORD dwFindLenchar *sigint len);

#define GetCallAddress(addr) (addr + (*(DWORD *)(addr+1)) + 5) 
hook.cpp

PHP Code:
#include <windows.h>
#include "hook.h"
#include "detours.h"

static hook_t *g_pHookBase;

hook_t *MH_NewHook(void)
{
    
hook_t *= new hook_t;
    
memset(h0sizeof(hook_t));
    
h->pNext g_pHookBase;
    
g_pHookBase h;
    return 
h;
}

void MH_FreeHook(hook_t *pHook)
{
    if (
pHook->pClass)
    {
        
tagVTABLEDATA *info = (tagVTABLEDATA *)pHook->pInfo;
        
MH_WriteMemory(info->pVFTInfoAddr, (BYTE *)pHook->pOldFuncAddrsizeof(DWORD));
    }
    else if (
pHook->hModule)
    {
        
tagIATDATA *info = (tagIATDATA *)pHook->pInfo;
        
MH_WriteMemory(info->pAPIInfoAddr, (BYTE *)pHook->pOldFuncAddrsizeof(DWORD));
    }
    else
    {
        
DetourTransactionBegin();
        
DetourUpdateThread(GetCurrentThread());
        
DetourDetach(&(void *&)pHook->pOldFuncAddrpHook->pNewFuncAddr);
        
DetourTransactionCommit();
    }

    if (
pHook->pInfo)
        
delete pHook->pInfo;

    
delete pHook;
}

void MH_FreeAllHook(void)
{
    
hook_t *next NULL;

    for (
hook_t *g_pHookBasehnext)
    {
        
next h->pNext;
        
MH_FreeHook(h);
    }

    
g_pHookBase NULL;
}

BOOL MH_UnHook(hook_t *pHook)
{
    if (!
g_pHookBase)
        return 
FALSE;

    if (!
g_pHookBase->pNext)
    {
        
MH_FreeHook(pHook);
        
g_pHookBase NULL;
        return 
TRUE;
    }

    
hook_t *last NULL;

    for (
hook_t *g_pHookBase->pNexthh->pNext)
    {
        if (
h->pNext != pHook)
        {
            
last h;
            continue;
        }

        
last->pNext h->pNext;
        
MH_FreeHook(h);
        return 
TRUE;
    }

    return 
FALSE;
}

hook_t *MH_InlineHook(void *pOldFuncAddrvoid *pNewFuncAddrvoid *&pCallBackFuncAddr)
{
    
hook_t *MH_NewHook();
    
h->pOldFuncAddr pOldFuncAddr;
    
h->pNewFuncAddr pNewFuncAddr;

    
DetourTransactionBegin();
    
DetourUpdateThread(GetCurrentThread());
    
DetourAttach(&(void *&)h->pOldFuncAddrpNewFuncAddr);
    
DetourTransactionCommit();

    
pCallBackFuncAddr h->pOldFuncAddr
    return 
h;
}

hook_t *MH_VFTHook(void *pClassint iTableIndexint iFuncIndexvoid *pNewFuncAddrvoid *&pCallBackFuncAddr)
{
    
tagVTABLEDATA *info = new tagVTABLEDATA;
    
info->pInstance = (tagCLASS *)pClass;

    
DWORD *pVMT = ((tagCLASS *)pClass iTableIndex)->pVMT;
    
info->pVFTInfoAddr pVMT iFuncIndex;

    
hook_t *MH_NewHook();
    
h->pOldFuncAddr = (void *)pVMT[iFuncIndex];
    
h->pNewFuncAddr pNewFuncAddr;
    
h->pInfo info;
    
h->pClass pClass;
    
h->iTableIndex iTableIndex;
    
h->iFuncIndex iFuncIndex;

    
pCallBackFuncAddr h->pOldFuncAddr;
    
MH_WriteMemory(info->pVFTInfoAddr, (BYTE *)&pNewFuncAddrsizeof(DWORD));
    return 
h;
}

DWORD MH_WriteMemory(void *pAddressBYTE *pDataDWORD dwDataSize)
{
    static 
DWORD dwProtect;

    if (
VirtualProtect(pAddressdwDataSizePAGE_EXECUTE_READWRITE, &dwProtect))
    {
        
memcpy(pAddresspDatadwDataSize);
        
VirtualProtect(pAddressdwDataSizedwProtect, &dwProtect);
    }

    return 
dwDataSize;
}

DWORD MH_ReadMemory(void *pAddressBYTE *pDataDWORD dwDataSize)
{
    static 
DWORD dwProtect;

    if (
VirtualProtect(pAddressdwDataSizePAGE_EXECUTE_READWRITE, &dwProtect))
    {
        
memcpy(pDatapAddressdwDataSize);
        
VirtualProtect(pAddressdwDataSizedwProtect, &dwProtect);
    }

    return 
dwDataSize;
}

DWORD MH_GetModuleBase(HMODULE hModule)
{
    
MEMORY_BASIC_INFORMATION mem;

    if (!
VirtualQuery(hModule, &memsizeof(MEMORY_BASIC_INFORMATION)))
        return 
0;

    return (
DWORD)mem.AllocationBase;
}

DWORD MH_GetModuleSize(HMODULE hModule)
{
    return ((
IMAGE_NT_HEADERS *)((DWORD)hModule + ((IMAGE_DOS_HEADER *)hModule)->e_lfanew))->OptionalHeader.SizeOfImage;
}

DWORD MH_SIGFind(DWORD dwStartAddrDWORD dwFindLenchar *sigint len)
{
    
DWORD dwEndAddr dwStartAddr dwFindLen len;
    
BOOL found;
    
char code;
    
int i;

    while (
dwStartAddr dwEndAddr)
    {
        
found TRUE;

        for (
0leni++)
        {
            
code = *(char *)(dwStartAddr i);

            if (
sig[i] != (char)0x2A && sig[i] != code)
            {
                
found FALSE;
                break;
            }
        }

        if (
found)
            return 
dwStartAddr;

        
dwStartAddr++;
    }

    return 
0;

Redirect your custom entity to an gamedll entity.

PHP Code:
ENTITYINIT GetEntityInit(char *pClassName)
{
    if(!
strcmp(pClassName"sky_camera") || !strcmp(pClassName"sky_box") || !strcmp(pClassName"sky_center") || !strcmp(pClassName"shadow_manager"))
    {
        return 
gHookFuncs.GetEntityInit("info_target");
    }
    return 
gHookFuncs.GetEntityInit(pClassName);

Dispatch their key values here or in AMXX plugin.

PHP Code:
void DispatchKeyValue_Post(edict_t *pentKeyvalueKeyValueData *pkvd)
{
    if(!
pentKeyvalue)
        
RETURN_META(MRES_IGNORED);
    if(!
pkvd->szClassName)
        
RETURN_META(MRES_IGNORED);

    if( !
strcmp(pkvd->szClassName"shadow_manager") )
    {
        if(!
strcmp(pkvd->szKeyName"affectmodel"))
            
pentKeyvalue->v.iuser1 MAKE_STRING(pkvd->szValue);
        else if(!
strcmp(pkvd->szKeyName"radius"))
            
pentKeyvalue->v.fuser1 atof(pkvd->szValue);
        else if(!
strcmp(pkvd->szKeyName"scale"))
            
pentKeyvalue->v.fuser2 atof(pkvd->szValue);
        else if(!
strcmp(pkvd->szKeyName"fard"))
            
pentKeyvalue->v.fuser3 atof(pkvd->szValue);
        else if(!
strcmp(pkvd->szKeyName"texsize"))
            
pentKeyvalue->v.iuser2 atoi(pkvd->szValue);
    }
    
RETURN_META(MRES_IGNORED);

Remember to call Engine_InstallHook in void OnAmxxAttach(void)

PHP Code:
void OnAmxxAttach(void)
{
    
Engine_InstallHook();

The sample code in attach files is a server-side plugin for my Meta Renderer mod, you can find this in moddb.
Attached Files
File Type: zip metarenderer_src.zip (90.9 KB, 145 views)
File Type: dll Renderer_amxx.dll (124.0 KB, 358 views)

Last edited by hzqst; 04-20-2015 at 04:42.
hzqst is offline
lein
Junior Member
Join Date: Mar 2013
Location: China
Old 04-20-2015 , 01:36   Re: Fake Custom Entity (win32)
Reply With Quote #2

消灭零回复!
__________________

一切伟大的行动和梦想,都有一个微不足道的开始!
All great actions and dreams, there is a small start!
lein is offline
KORD_12.7
Senior Member
Join Date: Aug 2009
Location: Russia, Vladivostok
Old 04-20-2015 , 04:36   Re: Fake Custom Entity (win32)
Reply With Quote #3

Nvm
__________________

Vi Veri Veniversum Vivus Vici
Russian Half-Life and Adrenaline Gamer community

Last edited by KORD_12.7; 04-20-2015 at 04:47.
KORD_12.7 is offline
Send a message via ICQ to KORD_12.7
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 01:07.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode