AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Module Coding (https://forums.alliedmods.net/forumdisplay.php?f=9)
-   -   Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX) (https://forums.alliedmods.net/showthread.php?t=293709)

meTaLiCroSS 02-08-2017 03:48

Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Time ago I've had troubles with hooking this function with Orpheu.

Now I tried to make a module to deal with this, by knowing it was some kind of weird optimization. It has the same behavior that SetAnimation function has. Reference: https://forums.alliedmods.net/showpo...postcount=1462

First, what I tried to do is to read Arkshine's way to deal with SetAnimation module: https://forums.alliedmods.net/showpo...51&postcount=7

Then I looked through the gamedll with IDA Pro and discover what does register store.

Inside CBasePlayer::PackDeadPlayerItems:

http://puu.sh/tTwl4/f0b324bdbe.png

and..:

http://puu.sh/tTwnh/ee60503a7b.png

What I guessed first was "ECX contains packAmmo parameter", which makes sense reading a decompiled version of PackDeadPlayerItems (it's always 1)

Then, reading this code, I tried to understand decompiled version of it.

PHP Code:

void packPlayerItem(CBasePlayer *pPlayerCBasePlayerItem *pItembool packAmmo)
{
    if (
pItem// ITEM CHECK FIRST
    
{
        const 
char *modelName GetCSModelName(pItem->m_iId);

        if (
modelName)
        {
            
CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create("weaponbox"pPlayer->pev->originpPlayer->pev->anglesENT(pPlayer->pev));
            
pWeaponBox->pev->angles.0;
            
pWeaponBox->pev->angles.0;
            
pWeaponBox->pev->velocity pPlayer->pev->velocity 0.75;
            
pWeaponBox->SetThink(&CWeaponBox::Kill);
            
pWeaponBox->pev->nextthink gpGlobals->time 300;
            
pWeaponBox->PackWeapon(pItem);

            if (
packAmmo// 3RD PARAM CONDITION
                
pWeaponBox->PackAmmo(MAKE_STRING(CBasePlayerItem::ItemInfoArray[pItem->m_iId].pszAmmo1), pPlayer->m_rgAmmo[pItem->PrimaryAmmoIndex()]);

            
SET_MODEL(ENT(pWeaponBox->pev), modelName);
        }
    }


Then, just with the decompiled version:

Code:
int __cdecl packPlayerItem(CBasePlayer *a1, CBasePlayerItem *a2, bool a3) {   int result; // eax@0   int v4; // edx@0   char v5; // cl@0   int v6; // ebx@1   int v7; // esi@1   int v8; // ebp@2   int v9; // edi@3   int v10; // edx@3   int v11; // eax@3   long double v12; // fst6@3   long double v13; // fst7@3   long double v14; // fst5@3   globalvars_t *v15; // edx@3   int v16; // eax@3   int v17; // eax@6   int this; // [sp+0h] [bp-3Ch]@0   edict_s *v19; // [sp+10h] [bp-2Ch]@0   char v20; // [sp+1Fh] [bp-1Dh]@1   v6 = result;   v7 = v4;   v20 = v5;   if ( v4 ) // if(pItem) ???   {     result = GetCSModelName(this);     v8 = result;     if ( result )     {       v9 = CBaseEntity::Create(              "weaponbox",              (char *)(*(_DWORD *)(v6 + 4) + 8),              (const Vector *)(*(_DWORD *)(v6 + 4) + 80),              *(const Vector **)(*(_DWORD *)(v6 + 4) + 520),              v19);       *(_DWORD *)(*(_DWORD *)(v9 + 4) + 80) = 0;       *(_DWORD *)(*(_DWORD *)(v9 + 4) + 88) = 0;       v10 = *(_DWORD *)(v6 + 4);       v11 = *(_DWORD *)(v9 + 4);       v12 = *(float *)(v10 + 40) * 0.75;       v13 = *(float *)(v10 + 36) * 0.75;       v14 = 0.75 * *(float *)(v10 + 32);       v15 = gpGlobals;       *(float *)(v11 + 32) = v14;       *(float *)(v11 + 36) = v13;       *(float *)(v11 + 40) = v12;       *(_DWORD *)(v9 + 16) = CWeaponBox::Kill;       v16 = *(_DWORD *)(v9 + 4);       *(_DWORD *)(v9 + 20) = 0;       *(float *)(v16 + 260) = v15->time + 300.0;       CWeaponBox::PackWeapon((CWeaponBox *)v9, (CBasePlayerItem *)v7);       if ( v20 ) // 3RD PARAM CONDITION       {         v17 = (*(int (__cdecl **)(int))(*(_DWORD *)v7 + 296))(v7);         CWeaponBox::PackAmmo(           (CWeaponBox *)v9,           *(_DWORD *)&CBasePlayerItem::ItemInfoArray[44 * *(_DWORD *)(v7 + 188) + 8]         - (unsigned int)gpGlobals->pStringBase,           *(_DWORD *)(v6 + 4 * v17 + 1524));       }       result = ((int (__cdecl *)(_DWORD, int))g_engfuncs.pfnSetModel)(*(_DWORD *)(*(_DWORD *)(v9 + 4) + 520), v8);     }   }   return result; }

Just read it and you'll understand.

v20 is packAmmo, v20 equals to v5 and v5 contains the value of CL register, lower part of ECX register, set as 1 in the pictures below. (also see this one)

v4 is pItem, and it points to EDX register.

Inside CBaseEntity::Create you can see references with v6 to the player pev->origin entvar. then you guess that v6 = result, and results references to the EAX register.

Now it makes sense this picture

http://puu.sh/tTwl4/f0b324bdbe.png

I managed to create this code, by reading a few guides of Inline Assembly in cpp and also Arkshine's SetAnimation hooker:

PHP Code:

//native Call_packPlayerItem(iId, iAnim)
static cell AMX_NATIVE_CALL Native_Call_packPlayerItem(AMX *amxcell *params)
{
    
voidpPlayer TypeConversion.id_to_cbase(params[1]);
    
    if(!
pPlayer)
    {
        
MF_LogError(amxAMX_ERR_NATIVE"Invalid player %d"params[1]);
        return 
0;
    }    
    
    
voidpItem TypeConversion.id_to_cbase(params[2]);
    
    if(!
pItem)
    {
        
MF_LogError(amxAMX_ERR_NATIVE"Invalid item %d"params[2]);
        return 
0;
    }
    
    
int packAmmo params[3];
    
    
SERVER_PRINT(UTIL_VarArgs("Calling packPlayerItem(%p, %p, %s)\n"pPlayerpItempackAmmo "TRUE" "FALSE"));    

    
asm volatile
    
(
        
"movl %0, %%eax;"
        "movl %1, %%edx;"
        "movl %2, %%ecx;"
        
/* no output */
        
"m" (pPlayer), "m" (pItem), "m" (packAmmo
        : 
"%eax""%edx""%ecx"
    
);
    
OrigFunc_packPlayerItem(pPlayerpItem, (bool)packAmmo);
    return 
1;


Retrieving it's address correctly also:

PHP Code:

void Patch_packPlayerItem()
{
    
OrigFunc_packPlayerItem Hooker->MemorySearchFunc_packPlayerItem >( "0x83,*,*,0x85,*,0x89,*,*,*,0x89,*,0x89,*,*,*,0x89,*,0x89,*,*,*,0x89", ( void* )MDLL_SpawnFALSE );

    if(
OrigFunc_packPlayerItem)
    {
        
// hooking was a hell also, so what I did was just to get function address
        /*Hook_packPlayerItem = Hooker->CreateHook( ( void* )OrigFunc_packPlayerItem, ( void* )OnpackPlayerItem, TRUE );

        if( Hook_packPlayerItem )
            SERVER_PRINT( "Hook created successfully\n" );
        else 
            SERVER_PRINT( "Hook creation failed\n" );*/
    
}
    else
        
SERVER_PRINT"Signature/symbol could not be found\n" );


Output throws correct values, function executed normally, weapon thrown and tested with players.

PHP Code:

L 02/08/2017 05:27:16"PM32<1><STEAM_0:0:nothankslol><>" joined team "CT"
Calling packPlayerItem(0xac022100xabfec30TRUE)
Calling packPlayerItem(0xac022100xac061e0TRUE)
Calling packPlayerItem(0xac022100xac06a10TRUE

The most FUNNY about about this is the next.

If I comment SERVER_PRINT call, server crashes with a seg fault. Why? I dont know.

So, my first guess is the inline assembler code that I wrote has a mistake, and I believe it's that. Am I doing right with it? Or am I forgetting something?

Thanks for reading.

Arkshine 02-08-2017 11:18

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
I don't know much the assembly language, but is it really needed the following: " : "%eax", "%edx", "%ecx" "?

PRoSToTeM@ 02-08-2017 12:08

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Try to declare OrigFunc_packPlayerItem with __attribute__((regparm(3))) before *.

Asmodai 02-08-2017 13:22

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Quote:

Originally Posted by Arkshine (Post 2493884)
I don't know much the assembly language, but is it really needed the following: " : "%eax", "%edx", "%ecx" "?

Yes, we must list all used registers because gcc didn't parsing asm insertions.

And your code from link
Code:

DETOUR_DECL_MEMBER1(SetAnimation, void, int, playerAnim)
{
        const void *pvPlayer = reinterpret_cast<const void*>(this);

#if defined(LINUX)

        asm volatile
        (
                "movl %%edx, %0;"
                "movl %%eax, %1;"
                : "=d" (playerAnim), "=a" (pvPlayer) : :
        );

#endif

        if (MF_ExecuteForward(OnSetAnimationForward, Utils::PrivateToIndex(pvPlayer), playerAnim) > 0)
        {
                return;
        }

#if defined(WIN32) || defined(APPLE)

        DETOUR_MEMBER_CALL(SetAnimation)(playerAnim);

#elif defined(LINUX)

        SetAnimationDetour->DisableDetour();
        SetAnimationOrig(pvPlayer, playerAnim);
        SetAnimationDetour->EnableDetour();

#endif
}

will not work, because eax and edx is a scratch registers that can be changed by any function call. You should change this registers only immediately before jmp to SetAnimation.

Arkshine 02-08-2017 16:41

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
The code above works, I don't post module which has not been tested. But it's possible I did not test in a way he would not work, I don't know. Also, I'm confused, I don't change the registers, I get the values from them and set them in variables.

Asmodai 02-08-2017 17:38

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Ah, right. I'm forgot about reverse operands order. Yes, in this way it's correct. I thought that you are calling original SetAnimation.

meTaLiCroSS 02-08-2017 17:45

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Quote:

Originally Posted by PRoSToTeM@ (Post 2493887)
Try to declare OrigFunc_packPlayerItem with __attribute__((regparm(3))) before *.

So it should be something like..

PHP Code:

typedef void __attribute__((regparm(3))) *Func_packPlayerItem )( void*, void*, bool );
Func_packPlayerItem OrigFunc_packPlayerItem NULL

?

Quote:

Originally Posted by Asmodai (Post 2493906)
Yes, we must list all used registers because gcc didn't parsing asm insertions.

And your code from link
Code:

DETOUR_DECL_MEMBER1(SetAnimation, void, int, playerAnim)
{
        const void *pvPlayer = reinterpret_cast<const void*>(this);

#if defined(LINUX)

        asm volatile
        (
                "movl %%edx, %0;"
                "movl %%eax, %1;"
                : "=d" (playerAnim), "=a" (pvPlayer) : :
        );

#endif

        if (MF_ExecuteForward(OnSetAnimationForward, Utils::PrivateToIndex(pvPlayer), playerAnim) > 0)
        {
                return;
        }

#if defined(WIN32) || defined(APPLE)

        DETOUR_MEMBER_CALL(SetAnimation)(playerAnim);

#elif defined(LINUX)

        SetAnimationDetour->DisableDetour();
        SetAnimationOrig(pvPlayer, playerAnim);
        SetAnimationDetour->EnableDetour();

#endif
}

will not work, because eax and edx is a scratch registers that can be changed by any function call. You should change this registers only immediately before jmp to SetAnimation.

He's doing right, because SetAnimation was split in 2. What Arkshine did was to retrieve inside's function address (the one which has optimization) and then inside the hook, it calls the original function, the one who calls the hooked inside, so he just need to retrieve values from register and he can call a normal function with it.

Also thanks for answering. The way I wrote the clubbed list is right then?

Quote:

Originally Posted by Arkshine (Post 2493963)
The code above works, I don't post module which has not been tested. But it's possible I did not test in a way he would not work, I don't know. Also, I'm confused, I don't change the registers, I get the values from them and set them in variables.

What I tested from your code was that if you retrieve these values from those register, they will be set to -1. If you retrieve eax and edx register 2 times and print his values, second call will throw -1, so I guess it has sense, but it's not wrong

PRoSToTeM@ 02-08-2017 18:00

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Quote:

Originally Posted by meTaLiCroSS (Post 2493983)
So it should be something like..

PHP Code:

typedef void __attribute__((regparm(3))) *Func_packPlayerItem )( void*, void*, bool );
Func_packPlayerItem OrigFunc_packPlayerItem NULL

?

Yep.

joropito 02-08-2017 19:20

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Didn't read all but the original function is cdecl so your function must be of cdecl type, remember that

meTaLiCroSS 02-08-2017 19:30

Re: Issue with packPlayerItem hooking (dealing with registers EAX-EDX-ECX)
 
Quote:

Originally Posted by PRoSToTeM@ (Post 2493986)
Yep.

I did it and it worked. I commented SERVER_PRINT, added this and worked without crash.

My final doubt is, does the "m" constraints are ok?


All times are GMT -4. The time now is 14:10.

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