UE4 - IDA定位查找GameInstance/Actors/ULevel
定位GameInstance
UPROPERTY()
class UGameInstance* OwningGameInstance;
他在UWorld类里面定义如上代码,但是我们要怎么找到他的偏移,神经病一样自问自答...
虚幻引擎内这个OwningGameInstance是私有的外部不能直接获取,是通过一个方法获取的,虽然编译后和直接获取没什么区别(
if (GetWorld() == nullptr)
{
UE_LOG(LogDemo, Error, TEXT("GetWorld() == nullptr"));
return false;
}
if (GetWorld()->GetGameInstance() == nullptr)
{
UE_LOG(LogDemo, Error, TEXT("GetWorld()->GetGameInstance() == nullptr"));
return false;
}
你看,这里有调用获取的方法,还带上了我们最喜欢的日志!虚幻引擎真的是太好玩了!
没啥意思,再见!
ULevel的定位
这里这个定位,我会介绍两种方法,一种是市面上比较泛滥的,一种是对照源码比较直观的!
第一种
这种方法比较不明所以,是我从网上学来的,虽然我不知道对应的源代码在什么地方,但是这个定位方法好像是依赖一块打印输出当初帧率CPU占用的代码的,
对GWorld进行xrefs,然后找下面有相同的两次CALL(arm64是BL)的汇编
然后我们IDA跳过去,兜兜转转又回到了这里,
第二种
我们看看下面的一个代码,这个是完整的代码,没有任何阉割!
GWorld = NewWorld;
WorldContext.SetCurrentWorld(NewWorld);
WorldContext.World()->WorldType = WorldContext.WorldType;
#if !UE_BUILD_SHIPPING
GWorld->bCreateRenderStateForHiddenComponents = bOldWorldWasShowingCollisionForHiddenComponents;
#endif
// Fixme: hacky but we need to set PackageFlags here if we are in a PIE Context.
// Also, don't add to root when in PIE, since PIE doesn't remove world from root
if (WorldContext.WorldType == EWorldType::PIE) // xxx == 3
{
check(WorldContext.World()->GetOutermost()->HasAnyPackageFlags(PKG_PlayInEditor));
WorldContext.World()->ClearFlags(RF_Standalone);
}
else
{
WorldContext.World()->AddToRoot();
}
// In the PIE case the world will already have been initialized as part of CreatePIEWorldByDuplication
if (!WorldContext.World()->bIsWorldInitialized)
{
WorldContext.World()->InitWorld();
}
// Handle pending level.
if( Pending )
{
check(Pending == WorldContext.PendingNetGame);
MovePendingLevel(WorldContext);
}
else
{
check(!WorldContext.World()->GetNetDriver());
}
WorldContext.World()->SetGameMode(URL);
if (FAudioDevice* AudioDevice = WorldContext.World()->GetAudioDevice())
{
AudioDevice->SetDefaultBaseSoundMix(WorldContext.World()->GetWorldSettings()->DefaultBaseSoundMix);
}
我们发现GWorld = NewWorld;
是一处赋值语句,我们定位出GWorld之后可以全局(就是去xrefs)去找这种赋值的操作,缩小一下查找范围,接下来我们找if (WorldContext.WorldType == EWorldType::PIE
)其实这在ida里面对应的就是
这在GWorld = NewWorld
之后必然有一个xxx == 3的操作,我们的目的是要找一下GetWorldSettings
的方法,下面是UWorld::GetWorldSettings
的源代码,我们发现里面对PersistentLevel
进行了调用。
AWorldSettings* UWorld::GetWorldSettings( const bool bCheckStreamingPersistent, const bool bChecked ) const
{
checkSlow(!IsInActualRenderingThread());
AWorldSettings* WorldSettings = nullptr;
if (PersistentLevel)
{
WorldSettings = PersistentLevel->GetWorldSettings(bChecked);
if( bCheckStreamingPersistent )
{
if( StreamingLevels.Num() > 0 &&
StreamingLevels[0] &&
StreamingLevels[0]->IsA<ULevelStreamingPersistent>())
{
ULevel* Level = StreamingLevels[0]->GetLoadedLevel();
if (Level != nullptr)
{
WorldSettings = Level->GetWorldSettings(bChecked);
}
}
}
}
return WorldSettings;
}
回到ida,对应的第一个Level::GetWorldSettings
的入参就是ULevel
啦!
Actor的定位
这个的定位其实最好是通过伟大的内存搜索器,比如CE、GG修改器什么的去做,因为在ULevel里面保存Actors的是一个TArray一个模板类,
class TARRAT<T> {
T* data;
dword size;
}
我们定位到ULevel之后,可以去翻内存,找一个指针,并且下面偏移8字节有一个数组大小的东西,那个大概率是Actors
(也可能是ActorsForGC
)... 回到正题!
else if (WorldType != EWorldType::Inactive && !IsRunningCommandlet())
{
// Inactive worlds do not have a world context, otherwise only worlds in the middle of seamless travel should have no context,
// and in that case, we shouldn't be destroying actors on them until they have become the current world (i.e. CopyWorldData has been called)
UE_LOG(LogSpawn, Warning, TEXT("UWorld::DestroyActor: World has no context! World: %s, Actor: %s"), *GetName(), *ThisActor->GetPathName());
}
// Remove the actor from the actor list.
RemoveActor( ThisActor, bShouldModifyLevel );
先贴一段源代码,我们搜索字符串UWorld::DestroyActor: World has no context!
。搜索这个干什么呢?为了找RemoveActor
!这对我们的大聪明来说轻而易举好吧,
这里贴一下UWorld::RemoveActor
源代码
void UWorld::RemoveActor(AActor* Actor, bool bShouldModifyLevel) const
{
ULevel* CheckLevel = Actor->GetLevel();
const int32 ActorListIndex = CheckLevel->Actors.Find( Actor );
// Search the entire list.
if( ActorListIndex != INDEX_NONE )
{
if ( bShouldModifyLevel && GUndo )
{
ModifyLevel( CheckLevel );
}
if (!IsGameWorld())
{
CheckLevel->Actors[ActorListIndex]->Modify();
}
CheckLevel->Actors[ActorListIndex] = nullptr;
}
// Remove actor from network list
RemoveNetworkActor( Actor );
}
因为呢编译器会把Find那个玩意内联优化,可能反汇编里面的结构不太一致,
但是我贴个这个总能看明白了吧!!!!!!!!!