TeamFortress2
起源引擎的游戏
如何定位s_pLocalPlayer 和 g_pEntityList.
起源引擎源代码
Finding g_pEntityList
Use IDA plugin "Class informer" to find this class: CSoundControllerImp.
.rdata:0000000000B69D20 ; class CSoundControllerImp: CSoundEnvelopeController, CAutoGameSystemPerFrame, CBaseGameSystemPerFrame, IGameSystemPerFrame, IGameSystem; [MI] (#classinformer)
.rdata:0000000000B69D20 dq offset ??_R4CSoundControllerImp@@6B@ ; const CSoundControllerImp::`RTTI Complete Object Locator'
.rdata:0000000000B69D28 ; const CSoundControllerImp::`vftable'
.rdata:0000000000B69D28 ??_7CSoundControllerImp@@6B@ dq offset sub_310C20
.rdata:0000000000B69D28 ; DATA XREF: sub_176B0+17↑o
.rdata:0000000000B69D30 dq offset sub_310CD0
.rdata:0000000000B69D38 dq offset sub_3109B0
.rdata:0000000000B69D40 dq offset sub_310B70
.rdata:0000000000B69D48 dq offset sub_310FB0
.rdata:0000000000B69D50 dq offset sub_311120
.rdata:0000000000B69D58 dq offset sub_311590
.rdata:0000000000B69D60 dq offset sub_311460
.rdata:0000000000B69D68 dq offset sub_311310
.rdata:0000000000B69D70 dq offset CSoundControllerImp__SoundCreate
.rdata:0000000000B69D78 dq offset sub_3116F0
.rdata:0000000000B69D80 dq offset sub_311740
.rdata:0000000000B69D88 dq offset sub_311750
.rdata:0000000000B69D90 dq offset CSoundControllerImp__SoundFadeOut
.rdata:0000000000B69D98 dq offset sub_311800
.rdata:0000000000B69DA0 dq offset sub_311810
.rdata:0000000000B69DA8 dq offset sub_311990
.rdata:0000000000B69DB0 dq offset CSoundControllerImp__SoundPlayEnvelope
.rdata:0000000000B69DB8 dq offset sub_3119B0
.rdata:0000000000B69DC0 dq offset sub_311820
.rdata:0000000000B69DC8 dq offset sub_311830
Find CSoundControllerImp::SoundCreate function in its vtable.
This is the source code.
CSoundPatch *CSoundControllerImp::SoundCreate( IRecipientFilter& filter, int nEntIndex, const char *pSoundName )
{
#ifdef CLIENT_DLLif ( GameRules() ){pSoundName = GameRules()->TranslateEffectForVisionFilter( "sounds", pSoundName );}
#endifCSoundPatch *pSound = new CSoundPatch;// FIXME: This is done so we don't have to futz with the public interfaceEHANDLE hEnt = (nEntIndex != -1) ? g_pEntityList->GetNetworkableHandle( nEntIndex ) : NULL;pSound->Init( &filter, hEnt.Get(), CHAN_AUTO, pSoundName, SNDLVL_NORM );return pSound;
}
This is the decompilation result.
__int64 __fastcall CSoundControllerImp::SoundCreate(__int64 this, __int64 filter, int nEntIndex, char *pSoundName)
{char *pSoundName_1; // rsi__int64 nEntIndex_1; // rdi_QWORD *pSound; // rax__int64 v8; // r8__int64 hEnt; // rbxpSoundName_1 = pSoundName;nEntIndex_1 = nEntIndex;if ( g_pGameRules )pSoundName_1 = (*(*g_pGameRules + 352LL))(g_pGameRules, "sounds", pSoundName);pSound = sub_92A000(dword_10F8790, 0x90u);v8 = 0;hEnt = pSound;if ( pSound ){*pSound = 0;*(pSound + 2) = 0;*(pSound + 12) = 0;pSound[2] = 0;*(pSound + 6) = 0;*(pSound + 28) = 0;*(pSound + 16) = -1;pSound[11] = &CCopyRecipientFilter::`vftable';*(pSound + 24) = 0;pSound[13] = 0;pSound[14] = 0;pSound[16] = pSound[13];*(pSound + 30) = 0;++dword_10F8690;pSound[6] = 0;pSound[7] = 0;*(pSound + 34) = *(qword_10F86D8 + 84);}else{hEnt = 0;}if ( nEntIndex_1 < 0x2000 )v8 = g_pEntityList[0][4 * nEntIndex_1 + 1]; // g_pEntityList->m_EntPtrArray[nEntIndex_1].m_pEntityCSoundPatch::Init(hEnt, filter, v8, 0, pSoundName_1, 75);return hEnt;
}
g_pEntityList found!!
Finding s_pLocalPlayer
In class informer, search for ClientModeShared.

Click on the first one.
Find a function looks like this
__int64 __fastcall ClientModeShared::CreateMove(__int64 this, float flInputSampleTime, __int64 cmd)
{__int64 pPlayer; // rax__int64 v5; // rdxpPlayer = C_BasePlayer::GetLocalPlayer();if ( pPlayer )return (*(*pPlayer + 1936LL))(pPlayer, v5, cmd);elsereturn 1;
}
bool ClientModeShared::CreateMove( float flInputSampleTime, CUserCmd *cmd )
{// Let the player override the view.C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();if(!pPlayer)return true;// Let the player at itreturn pPlayer->CreateMove( flInputSampleTime, cmd );
}
Follow C_BasePlayer::GetLocalPlayer()
__int64 C_BasePlayer::GetLocalPlayer()
{return s_pLocalPlayer;
}
Then we have it, s_pLocalPlayer!!
CClientEntityList::GetClientEntity reverse
for (auto i = 1; i <= 64; ++i)
{const auto entity = entityList->GetClientEntity(i);if (!entity) {continue;}//...
}
在internel cheat里面, 通常用这样一段代码遍历所有实体, GetClientEntity就是这个关键函数。
IClientEntity* CClientEntityList::GetClientEntity( int entnum )
{IClientUnknown *pEnt = GetListedEntity( entnum );return pEnt ? pEnt->GetIClientEntity() : 0;
}
inline IClientUnknown* CClientEntityList::GetListedEntity( int entnum )
{return (IClientUnknown*)LookupEntityByNetworkIndex( entnum );
}
inline IHandleEntity* CBaseEntityList::LookupEntityByNetworkIndex( int edictIndex ) const
{// (Legacy support).if ( edictIndex < 0 )return NULL;Assert( edictIndex < NUM_ENT_ENTRIES );return (IHandleEntity*)m_EntPtrArray[edictIndex].m_pEntity;
}
GetIClientEntity()有多个实现, 但是只有这两种
virtual IClientEntity* GetIClientEntity() { return 0; }
和
virtual IClientEntity* GetIClientEntity() { return this; }
直接当作return this;
最终这一坨被编译器优化成了这个。
__int64 __fastcall CClientEntityList::GetClientEntity(__int64 this, int entnum)
{__int64 v2; // rcxif ( entnum <= 0x1FFF && (v2 = *(32 * (entnum - 8193LL) + this)) != 0 )return (*(*v2 + 48LL))(v2);elsereturn 0;
}
未完