UE4逆向篇-1_FNamePool

写在前面

1.UE4.23以下与4.23以上版本是有所区别的,笔者所使用的UE源码为4.27。

2.UE4为开源的GitHub项目,但你想下载其源码需要将epic账号与GitHub账号绑定,其次会收到一封邮件以加入GitHub组织。

https://github.com/EpicGames

一、开始之前

1.由于C++没有反射,而UE自行实现的反射导致其暴露出了非常多的接口,所以我们可以利用这些暴露出来的接口。

2.GNames是Unicode字符串的数组。

3.GObject是类指针的数组。

拥有GObjects以后,可以访问游戏中大部分对象,而GName则对应其对象的名称?

拥有这些信息以后,可以生成出一个SDK来修改游戏。

二、GNames

a.什么是Gnames

1.GNames 指的是 alignas(FNamePool) static uint8 NamePoolData[sizeof(FNamePool)];

2.也就是NamePoolData

3.它是一个数组结构。

b.NamePoolData/FNamePool

以下是该结构体原型:


class FNamePool
{
public:
	FNamePool();
	
	void			Reserve(uint32 NumBlocks, uint32 NumEntries);
	FNameEntryId	Store(FNameStringView View);
	FNameEntryId	Find(FNameStringView View) const;
	FNameEntryId	Find(EName Ename) const;
	const EName*	FindEName(FNameEntryId Id) const;

	/** @pre !!Handle */
	FNameEntry&		Resolve(FNameEntryHandle Handle) const { return Entries.Resolve(Handle); }

	bool			IsValid(FNameEntryHandle Handle) const;

	FNameEntryId	StoreValue(const FNameComparisonValue& Value);
	void			StoreBatch(uint32 ShardIdx, TArrayView<FNameComparisonLoad> Batch)	{ ComparisonShards[ShardIdx].InsertBatch(Batch); }
#if WITH_CASE_PRESERVING_NAME
	FNameEntryId	StoreValue(const FNameDisplayValue& Value, bool bReuseComparisonId);
	void			StoreBatch(uint32 ShardIdx, TArrayView<FNameDisplayLoad> Batch)		{ DisplayShards[ShardIdx].InsertBatch(Batch); }
	bool			ReuseComparisonEntry(bool bAddedComparisonEntry, const FNameDisplayValue& DisplayValue);
#endif
	/// Stats and debug related functions ///

	uint32			NumEntries() const;
	uint32			NumAnsiEntries() const;
	uint32			NumWideEntries() const;
	uint32			NumBlocks() const { return Entries.NumBlocks(); }
	uint32			NumSlots() const;
	void			LogStats(FOutputDevice& Ar) const;
	uint8**			GetBlocksForDebugVisualizer() { return Entries.GetBlocksForDebugVisualizer(); }
	TArray<const FNameEntry*> DebugDump() const;

private:
	enum { MaxENames = 512 };

	FNameEntryAllocator Entries;

#if WITH_CASE_PRESERVING_NAME
	FNamePoolShard<ENameCase::CaseSensitive> DisplayShards[FNamePoolShards];
#endif
	FNamePoolShard<ENameCase::IgnoreCase> ComparisonShards[FNamePoolShards];

	// Put constant lookup on separate cache line to avoid it being constantly invalidated by insertion
	alignas(PLATFORM_CACHE_LINE_SIZE) FNameEntryId ENameToEntry[NAME_MaxHardcodedNameIndex] = {};
	uint32 LargestEnameUnstableId;
	TMap<FNameEntryId, EName, TInlineSetAllocator<MaxENames>> EntryToEName;
};

c.FNameEntryAllocator

1.FNamePool结构体之中的大部分我们都可以忽略。

2.更需要重点关注其中的:FNameEntryAllocator Entries;


class FNamePool
{
private:
	FNameEntryAllocator Entries;
};

需要注意 FNameEntryAllocator 并不是一个指针,而是完整的结构体。

可以将其视作为FNamePool。

FNamePool = FNameEntryAllocator。

同样的,该结构体中的大部分都可以忽略,重点关注以下内容:

static constexpr uint32 FNameMaxBlocks = 1 << FNameMaxBlockBits;
class FNameEntryAllocator
{
	mutable FRWLock Lock;                    //0x0000
	uint32 CurrentBlock = 0;                 //0x0008
	uint32 CurrentByteCursor = 0;            //0x000C
	uint8* Blocks[FNameMaxBlocks] = {};      //0x0010
};

FName中存储的其实就是一堆字符串,而字符串中的内容是什么样的呢?

接下来我们开始查找它。

三、查找NamePoolData

1.想要查找NamePoolData其实非常简单,只需要打开游戏并使用CE附加游戏。

2.搜索字符串:MulticastDelegateProperty,注意不要勾选UTF-16(宽字符)。

3.搜索字符串后浏览内存相关区域,快捷键Ctrl+B,并向上翻:

看到内存区域类似.None.....ByteProperty.....IntProperty即可。

4.此时从None往前推6个字节,即:......None,并将该地址记录下来:

记录下该地址,回到CE中进行新的扫描,勾选HEX扫描8字节:

如果扫描不到可以尝试调整CE的可写选项

注意:

可尝试先前推2个字节拿到地址进行搜索,如果搜索不到再向前推更多字节。

如该游戏前推2个字节搜索不到结果,笔者这里直接将地址的最后一位用0替换(也就是前推6个字节),即可搜索到了。

不同的游戏可能不一样。

扫描出的地址:"DeadByDaylight-Win64-Shipping.exe"+D20C610

其指向的就是 FNameEntryAllocator 中的 uint8* Blocks[FNameMaxBlocks] = {};

static constexpr uint32 FNameMaxBlocks = 1 << FNameMaxBlockBits;
class FNameEntryAllocator
{
	mutable FRWLock Lock;                    //0x0000
	uint32 CurrentBlock = 0;                 //0x0008
	uint32 CurrentByteCursor = 0;            //0x000C
	uint8* Blocks[FNameMaxBlocks] = {};      //0x0010
};

5.现在,只需要使用扫描出的地址减去 Blocks(uint8* Blocks[FNameMaxBlocks] = {};) 的偏移0x10,即可得出FNamePool,使用ReClass验证一下:

1.打开ReClass并打开文件选择附件里的FNamePool.reclass。

2.点击Select按钮选择游戏进程。

3.点击Edit按钮,选择FNamePool,并将地址填入进行验证。

至此,查找NamePoolData的工作就完成了,通过修改UnrealDumper 的源码即可导出Names:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值